// distribution boxbackup-0.10 (svn version: 494)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must
// display the following acknowledgment:
// This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// [Where legally impermissible the Authors do not disclaim liability for
// direct physical injury or death caused solely by defects in the software
// unless it is modified by a third party.]
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
//
// --------------------------------------------------------------------------
//
// File
// Name: BackupStoreFile.cpp
// Purpose: Utils for manipulating files
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
#include "Box.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <string.h>
#include <new>
#include <string.h>
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
#ifndef WIN32
#include <syslog.h>
#endif
#include <stdio.h>
#endif
#include "BackupStoreFile.h"
#include "BackupStoreFileWire.h"
#include "BackupStoreFileCryptVar.h"
#include "BackupStoreFilename.h"
#include "BackupStoreException.h"
#include "IOStream.h"
#include "Guards.h"
#include "FileModificationTime.h"
#include "FileStream.h"
#include "BackupClientFileAttributes.h"
#include "BackupStoreObjectMagic.h"
#include "Compress.h"
#include "CipherContext.h"
#include "CipherBlowfish.h"
#include "CipherAES.h"
#include "BackupStoreConstants.h"
#include "CollectInBufferStream.h"
#include "RollingChecksum.h"
#include "MD5Digest.h"
#include "ReadGatherStream.h"
#include "Random.h"
#include "BackupStoreFileEncodeStream.h"
#include "MemLeakFindOn.h"
using namespace BackupStoreFileCryptVar;
// How big a buffer to use for copying files
#define COPY_BUFFER_SIZE (8*1024)
// Statistics
BackupStoreFileStats BackupStoreFile::msStats = {0,0,0};
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
bool sWarnedAboutBackwardsCompatiblity = false;
#endif
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::EncodeFile(IOStream &, IOStream &)
// Purpose: Encode a file into something for storing on file server.
// Requires a real filename so full info can be stored.
//
// Returns a stream. Most of the work is done by the stream
// when data is actually requested -- the file will be held
// open until the stream is deleted or the file finished.
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
std::auto_ptr<IOStream> BackupStoreFile::EncodeFile(const char *Filename, int64_t ContainerID, const BackupStoreFilename &rStoreFilename, int64_t *pModificationTime)
{
// Create the stream
std::auto_ptr<IOStream> stream(new BackupStoreFileEncodeStream);
// Do the initial setup
((BackupStoreFileEncodeStream*)stream.get())->Setup(Filename, 0 /* no recipe, just encode */,
ContainerID, rStoreFilename, pModificationTime);
// Return the stream for the caller
return stream;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::VerifyEncodedFileFormat(IOStream &)
// Purpose: Verify that an encoded file meets the format requirements.
// Doesn't verify that the data is intact and can be decoded.
// Optionally returns the ID of the file which it is diffed from,
// and the (original) container ID.
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
bool BackupStoreFile::VerifyEncodedFileFormat(IOStream &rFile, int64_t *pDiffFromObjectIDOut, int64_t *pContainerIDOut)
{
// Get the size of the file
int64_t fileSize = rFile.BytesLeftToRead();
if(fileSize == IOStream::SizeOfStreamUnknown)
{
THROW_EXCEPTION(BackupStoreException, StreamDoesntHaveRequiredFeatures)
}
// Get the header...
file_StreamFormat hdr;
if(!rFile.ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */))
{
// Couldn't read header
return false;
}
// Check magic number
if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
&& ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
#endif
)
{
return false;
}
// Get a filename, see if it loads OK
try
{
BackupStoreFilename fn;
fn.ReadFromStream(rFile, IOStream::TimeOutInfinite);
}
catch(...)
{
// an error occured while reading it, so that's not good
return false;
}
// Skip the attributes -- because they're encrypted, the server can't tell whether they're OK or not
try
{
int32_t size_s;
if(!rFile.ReadFullBuffer(&size_s, sizeof(size_s), 0 /* not interested in bytes read if this fails */))
{
THROW_EXCEPTION(CommonException, StreamableMemBlockIncompleteRead)
}
int size = ntohl(size_s);
// Skip forward the size
rFile.Seek(size, IOStream::SeekType_Relative);
}
catch(...)
{
// an error occured while reading it, so that's not good
return false;
}
// Get current position in file -- the end of the header
int64_t headerEnd = rFile.GetPosition();
// Get number of blocks
int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
// Calculate where the block index will be, check it's reasonable
int64_t blockIndexLoc = fileSize - ((numBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader));
if(blockIndexLoc < headerEnd)
{
// Not enough space left for the block index, let alone the blocks themselves
return false;
}
// Load the block index header
rFile.Seek(blockIndexLoc, IOStream::SeekType_Absolute);
file_BlockIndexHeader blkhdr;
if(!rFile.ReadFullBuffer(&blkhdr, sizeof(blkhdr), 0 /* not interested in bytes read if this fails */))
{
// Couldn't read block index header -- assume bad file
return false;
}
// Check header
if((ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
&& ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0
#endif
)
|| (int64_t)box_ntoh64(blkhdr.mNumBlocks) != numBlocks)
{
// Bad header -- either magic value or number of blocks is wrong
return false;
}
// Flag for recording whether a block is referenced from another file
bool blockFromOtherFileReferenced = false;
// Read the index, checking that the length values all make sense
int64_t currentBlockStart = headerEnd;
for(int64_t b = 0; b < numBlocks; ++b)
{
// Read block entry
file_BlockIndexEntry blk;
if(!rFile.ReadFullBuffer(&blk, sizeof(blk), 0 /* not interested in bytes read if this fails */))
{
// Couldn't read block index entry -- assume bad file
return false;
}
// Check size and location
int64_t blkSize = box_ntoh64(blk.mEncodedSize);
if(blkSize <= 0)
{
// Mark that this file references another file
blockFromOtherFileReferenced = true;
}
else
{
// This block is actually in this file
if((currentBlockStart + blkSize) > blockIndexLoc)
{
// Encoded size makes the block run over the index
return false;
}
// Move the current block start ot the end of this block
currentBlockStart += blkSize;
}
}
// Check that there's no empty space
if(currentBlockStart != blockIndexLoc)
{
return false;
}
// Check that if another block is references, then the ID is there, and if one isn't there is no ID.
int64_t otherID = box_ntoh64(blkhdr.mOtherFileID);
if((otherID != 0 && blockFromOtherFileReferenced == false)
|| (otherID == 0 && blockFromOtherFileReferenced == true))
{
// Doesn't look good!
return false;
}
// Does the caller want the other ID?
if(pDiffFromObjectIDOut)
{
*pDiffFromObjectIDOut = otherID;
}
// Does the caller want the container ID?
if(pContainerIDOut)
{
*pContainerIDOut = box_ntoh64(hdr.mContainerID);
}
// Passes all tests
return true;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodeFile(IOStream &, const char *)
// Purpose: Decode a file. Will set file attributes. File must not exist.
// Created: 2003/08/28
//
// --------------------------------------------------------------------------
void BackupStoreFile::DecodeFile(IOStream &rEncodedFile, const char *DecodedFilename, int Timeout, const BackupClientFileAttributes *pAlterativeAttr)
{
// Does file exist?
struct stat st;
if(::stat(DecodedFilename, &st) == 0)
{
THROW_EXCEPTION(BackupStoreException, OutputFileAlreadyExists)
}
// Try, delete output file if error
try
{
// Make a stream for outputting this file
FileStream out(DecodedFilename, O_WRONLY | O_CREAT | O_EXCL);
// Get the decoding stream
std::auto_ptr<DecodedStream> stream(DecodeFileStream(rEncodedFile, Timeout, pAlterativeAttr));
// Is it a symlink?
if(!stream->IsSymLink())
{
// Copy it out to the file
stream->CopyStreamTo(out);
}
// Write the attributes
stream->GetAttributes().WriteAttributes(DecodedFilename);
}
catch(...)
{
::unlink(DecodedFilename);
throw;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodeFileStream(IOStream &, int, const BackupClientFileAttributes *)
// Purpose: Return a stream which will decode the encrypted file data on the fly.
// Accepts streams in block index first, or main header first, order. In the latter case,
// the stream must be Seek()able.
//
// Before you use the returned stream, call IsSymLink() -- symlink streams won't allow
// you to read any data to enforce correct logic. See BackupStoreFile::DecodeFile() implementation.
// Created: 9/12/03
//
// --------------------------------------------------------------------------
std::auto_ptr<BackupStoreFile::DecodedStream> BackupStoreFile::DecodeFileStream(IOStream &rEncodedFile, int Timeout, const BackupClientFileAttributes *pAlterativeAttr)
{
// Create stream
std::auto_ptr<DecodedStream> stream(new DecodedStream(rEncodedFile, Timeout));
// Get it ready
stream->Setup(pAlterativeAttr);
// Return to caller
return stream;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::DecodedStream(IOStream &, int)
// Purpose: Constructor
// Created: 9/12/03
//
// --------------------------------------------------------------------------
BackupStoreFile::DecodedStream::DecodedStream(IOStream &rEncodedFile, int Timeout)
: mrEncodedFile(rEncodedFile),
mTimeout(Timeout),
mNumBlocks(0),
mpBlockIndex(0),
mpEncodedData(0),
mpClearData(0),
mClearDataSize(0),
mCurrentBlock(-1),
mCurrentBlockClearSize(0),
mPositionInCurrentBlock(0),
mEntryIVBase(42) // different to default value in the encoded stream!
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
, mIsOldVersion(false)
#endif
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::~DecodedStream()
// Purpose: Desctructor
// Created: 9/12/03
//
// --------------------------------------------------------------------------
BackupStoreFile::DecodedStream::~DecodedStream()
{
// Free any allocated memory
if(mpBlockIndex)
{
::free(mpBlockIndex);
}
if(mpEncodedData)
{
BackupStoreFile::CodingChunkFree(mpEncodedData);
}
if(mpClearData)
{
::free(mpClearData);
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::Setup(const BackupClientFileAttributes *)
// Purpose: Get the stream ready to decode -- reads in headers
// Created: 9/12/03
//
// --------------------------------------------------------------------------
void BackupStoreFile::DecodedStream::Setup(const BackupClientFileAttributes *pAlterativeAttr)
{
// Get the size of the file
int64_t fileSize = mrEncodedFile.BytesLeftToRead();
// Get the magic number to work out which order the stream is in
int32_t magic;
if(!mrEncodedFile.ReadFullBuffer(&magic, sizeof(magic), 0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read magic value
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
bool inFileOrder = true;
switch(ntohl(magic))
{
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
case OBJECTMAGIC_FILE_MAGIC_VALUE_V0:
mIsOldVersion = true;
// control flows on
#endif
case OBJECTMAGIC_FILE_MAGIC_VALUE_V1:
inFileOrder = true;
break;
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
case OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0:
mIsOldVersion = true;
// control flows on
#endif
case OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1:
inFileOrder = false;
break;
default:
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// If not in file order, then the index list must be read now
if(!inFileOrder)
{
ReadBlockIndex(true /* have already read and verified the magic number */);
}
// Get header
file_StreamFormat hdr;
if(inFileOrder)
{
// Read the header, without the magic number
if(!mrEncodedFile.ReadFullBuffer(((uint8_t*)&hdr) + sizeof(magic), sizeof(hdr) - sizeof(magic),
0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
// Put in magic number
hdr.mMagicValue = magic;
}
else
{
// Not in file order, so need to read the full header
if(!mrEncodedFile.ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
}
// Check magic number
if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
&& ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
#endif
)
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// Get the filename
mFilename.ReadFromStream(mrEncodedFile, mTimeout);
// Get the attributes (either from stream, or supplied attributes)
if(pAlterativeAttr != 0)
{
// Read dummy attributes
BackupClientFileAttributes attr;
attr.ReadFromStream(mrEncodedFile, mTimeout);
// Set to supplied attributes
mAttributes = *pAlterativeAttr;
}
else
{
// Read the attributes from the stream
mAttributes.ReadFromStream(mrEncodedFile, mTimeout);
}
// If it is in file order, go and read the file attributes
// Requires that the stream can seek
if(inFileOrder)
{
// Make sure the file size is known
if(fileSize == IOStream::SizeOfStreamUnknown)
{
THROW_EXCEPTION(BackupStoreException, StreamDoesntHaveRequiredFeatures)
}
// Store current location (beginning of encoded blocks)
int64_t endOfHeaderPos = mrEncodedFile.GetPosition();
// Work out where the index is
int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
int64_t blockHeaderPos = fileSize - ((numBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader));
// Seek to that position
mrEncodedFile.Seek(blockHeaderPos, IOStream::SeekType_Absolute);
// Read the block index
ReadBlockIndex(false /* magic number still to be read */);
// Seek back to the end of header position, ready for reading the chunks
mrEncodedFile.Seek(endOfHeaderPos, IOStream::SeekType_Absolute);
}
// Check view of blocks from block header and file header match
if(mNumBlocks != (int64_t)box_ntoh64(hdr.mNumBlocks))
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// Need to allocate some memory for the two blocks for reading encoded data, and clear data
if(mNumBlocks > 0)
{
// Find the maximum encoded data size
int32_t maxEncodedDataSize = 0;
const file_BlockIndexEntry *entry = (file_BlockIndexEntry *)mpBlockIndex;
ASSERT(entry != 0);
for(int64_t e = 0; e < mNumBlocks; e++)
{
// Get the clear and encoded size
int32_t encodedSize = box_ntoh64(entry[e].mEncodedSize);
ASSERT(encodedSize > 0);
// Larger?
if(encodedSize > maxEncodedDataSize) maxEncodedDataSize = encodedSize;
}
// Allocate those blocks!
mpEncodedData = (uint8_t*)BackupStoreFile::CodingChunkAlloc(maxEncodedDataSize + 32);
// Allocate the block for the clear data, using the hint from the header.
// If this is wrong, things will exception neatly later on, so it can't be used
// to do anything more than cause an error on downloading.
mClearDataSize = OutputBufferSizeForKnownOutputSize(ntohl(hdr.mMaxBlockClearSize)) + 32;
mpClearData = (uint8_t*)::malloc(mClearDataSize);
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::ReadBlockIndex(bool)
// Purpose: Read the block index from the stream, and store in internal buffer (minus header)
// Created: 9/12/03
//
// --------------------------------------------------------------------------
void BackupStoreFile::DecodedStream::ReadBlockIndex(bool MagicAlreadyRead)
{
// Header
file_BlockIndexHeader blkhdr;
// Read it in -- way depends on how whether the magic number has already been read
if(MagicAlreadyRead)
{
// Read the header, without the magic number
if(!mrEncodedFile.ReadFullBuffer(((uint8_t*)&blkhdr) + sizeof(blkhdr.mMagicValue), sizeof(blkhdr) - sizeof(blkhdr.mMagicValue),
0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
}
else
{
// Magic not already read, so need to read the full header
if(!mrEncodedFile.ReadFullBuffer(&blkhdr, sizeof(blkhdr), 0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
// Check magic value
if(ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
&& ntohl(blkhdr.mMagicValue) != OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0
#endif
)
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
}
// Get the number of blocks out of the header
mNumBlocks = box_ntoh64(blkhdr.mNumBlocks);
// Read the IV base
mEntryIVBase = box_ntoh64(blkhdr.mEntryIVBase);
// Load the block entries in?
if(mNumBlocks > 0)
{
// How big is the index?
int64_t indexSize = sizeof(file_BlockIndexEntry) * mNumBlocks;
// Allocate some memory
mpBlockIndex = ::malloc(indexSize);
if(mpBlockIndex == 0)
{
throw std::bad_alloc();
}
// Read it in
if(!mrEncodedFile.ReadFullBuffer(mpBlockIndex, indexSize, 0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::Read(void *, int, int)
// Purpose: As interface. Reads decrpyted data.
// Created: 9/12/03
//
// --------------------------------------------------------------------------
int BackupStoreFile::DecodedStream::Read(void *pBuffer, int NBytes, int Timeout)
{
// Symlinks don't have data. So can't read it. Not even zero bytes.
if(IsSymLink())
{
// Don't allow reading in this case
THROW_EXCEPTION(BackupStoreException, ThereIsNoDataInASymLink);
}
// Already finished?
if(mCurrentBlock >= mNumBlocks)
{
// At end of stream, nothing to do
return 0;
}
int bytesToRead = NBytes;
uint8_t *output = (uint8_t*)pBuffer;
while(bytesToRead > 0 && mCurrentBlock < mNumBlocks)
{
// Anything left in the current block?
if(mPositionInCurrentBlock < mCurrentBlockClearSize)
{
// Copy data out of this buffer
int s = mCurrentBlockClearSize - mPositionInCurrentBlock;
if(s > bytesToRead) s = bytesToRead; // limit to requested data
// Copy
::memcpy(output, mpClearData + mPositionInCurrentBlock, s);
// Update positions
output += s;
mPositionInCurrentBlock += s;
bytesToRead -= s;
}
// Need to get some more data?
if(bytesToRead > 0 && mPositionInCurrentBlock >= mCurrentBlockClearSize)
{
// Number of next block
++mCurrentBlock;
if(mCurrentBlock >= mNumBlocks)
{
// Stop now!
break;
}
// Get the size from the block index
const file_BlockIndexEntry *entry = (file_BlockIndexEntry *)mpBlockIndex;
int32_t encodedSize = box_ntoh64(entry[mCurrentBlock].mEncodedSize);
if(encodedSize <= 0)
{
// The caller is attempting to decode a file which is the direct result of a diff
// operation, and so does not contain all the data.
// It needs to be combined with the previous version first.
THROW_EXCEPTION(BackupStoreException, CannotDecodeDiffedFilesWithoutCombining)
}
// Load in next block
if(!mrEncodedFile.ReadFullBuffer(mpEncodedData, encodedSize, 0 /* not interested in bytes read if this fails */, mTimeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, WhenDecodingExpectedToReadButCouldnt)
}
// Decode the data
mCurrentBlockClearSize = BackupStoreFile::DecodeChunk(mpEncodedData, encodedSize, mpClearData, mClearDataSize);
// Calculate IV for this entry
uint64_t iv = mEntryIVBase;
iv += mCurrentBlock;
// Convert to network byte order before encrypting with it, so that restores work on
// platforms with different endiannesses.
iv = box_hton64(iv);
sBlowfishDecryptBlockEntry.SetIV(&iv);
// Decrypt the encrypted section
file_BlockIndexEntryEnc entryEnc;
int sectionSize = sBlowfishDecryptBlockEntry.TransformBlock(&entryEnc, sizeof(entryEnc),
entry[mCurrentBlock].mEnEnc, sizeof(entry[mCurrentBlock].mEnEnc));
if(sectionSize != sizeof(entryEnc))
{
THROW_EXCEPTION(BackupStoreException, BlockEntryEncodingDidntGiveExpectedLength)
}
// Make sure this is the right size
if(mCurrentBlockClearSize != (int32_t)ntohl(entryEnc.mSize))
{
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
if(!mIsOldVersion)
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// Versions 0.05 and previous of Box Backup didn't properly handle endianess of the
// IV for the encrypted section. Try again, with the thing the other way round
iv = box_swap64(iv);
sBlowfishDecryptBlockEntry.SetIV(&iv);
int sectionSize = sBlowfishDecryptBlockEntry.TransformBlock(&entryEnc, sizeof(entryEnc),
entry[mCurrentBlock].mEnEnc, sizeof(entry[mCurrentBlock].mEnEnc));
if(sectionSize != sizeof(entryEnc))
{
THROW_EXCEPTION(BackupStoreException, BlockEntryEncodingDidntGiveExpectedLength)
}
if(mCurrentBlockClearSize != (int32_t)ntohl(entryEnc.mSize))
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
else
{
// Warn and log this issue
if(!sWarnedAboutBackwardsCompatiblity)
{
::printf("WARNING: Decoded one or more files using backwards compatibility mode for block index.\n");
::syslog(LOG_ERR, "WARNING: Decoded one or more files using backwards compatibility mode for block index.\n");
sWarnedAboutBackwardsCompatiblity = true;
}
}
#else
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
#endif
}
// Check the digest
MD5Digest md5;
md5.Add(mpClearData, mCurrentBlockClearSize);
md5.Finish();
if(!md5.DigestMatches((uint8_t*)entryEnc.mStrongChecksum))
{
THROW_EXCEPTION(BackupStoreException, BackupStoreFileFailedIntegrityCheck)
}
// Set vars to say what's happening
mPositionInCurrentBlock = 0;
}
}
ASSERT(bytesToRead >= 0);
ASSERT(bytesToRead <= NBytes);
return NBytes - bytesToRead;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::IsSymLink()
// Purpose: Is the unencoded file actually a symlink?
// Created: 10/12/03
//
// --------------------------------------------------------------------------
bool BackupStoreFile::DecodedStream::IsSymLink()
{
// First, check in with the attributes
if(!mAttributes.IsSymLink())
{
return false;
}
// So the attributes think it is a symlink.
// Consistency check...
if(mNumBlocks != 0)
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
return true;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::Write(const void *, int)
// Purpose: As interface. Throws exception, as you can't write to this stream.
// Created: 9/12/03
//
// --------------------------------------------------------------------------
void BackupStoreFile::DecodedStream::Write(const void *pBuffer, int NBytes)
{
THROW_EXCEPTION(BackupStoreException, CantWriteToDecodedFileStream)
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::StreamDataLeft()
// Purpose: As interface. Any data left?
// Created: 9/12/03
//
// --------------------------------------------------------------------------
bool BackupStoreFile::DecodedStream::StreamDataLeft()
{
return mCurrentBlock < mNumBlocks;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodedStream::StreamClosed()
// Purpose: As interface. Always returns true, no writing allowed.
// Created: 9/12/03
//
// --------------------------------------------------------------------------
bool BackupStoreFile::DecodedStream::StreamClosed()
{
// Can't write to this stream!
return true;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::SetBlowfishKey(const void *, int)
// Purpose: Static. Sets the key to use for encryption and decryption.
// Created: 7/12/03
//
// --------------------------------------------------------------------------
void BackupStoreFile::SetBlowfishKeys(const void *pKey, int KeyLength, const void *pBlockEntryKey, int BlockEntryKeyLength)
{
// IVs set later
sBlowfishEncrypt.Reset();
sBlowfishEncrypt.Init(CipherContext::Encrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
sBlowfishDecrypt.Reset();
sBlowfishDecrypt.Init(CipherContext::Decrypt, CipherBlowfish(CipherDescription::Mode_CBC, pKey, KeyLength));
sBlowfishEncryptBlockEntry.Reset();
sBlowfishEncryptBlockEntry.Init(CipherContext::Encrypt, CipherBlowfish(CipherDescription::Mode_CBC, pBlockEntryKey, BlockEntryKeyLength));
sBlowfishEncryptBlockEntry.UsePadding(false);
sBlowfishDecryptBlockEntry.Reset();
sBlowfishDecryptBlockEntry.Init(CipherContext::Decrypt, CipherBlowfish(CipherDescription::Mode_CBC, pBlockEntryKey, BlockEntryKeyLength));
sBlowfishDecryptBlockEntry.UsePadding(false);
}
#ifndef HAVE_OLD_SSL
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::SetAESKey(const void *, int)
// Purpose: Sets the AES key to use for file data encryption. Will select AES as
// the cipher to use when encrypting.
// Created: 27/4/04
//
// --------------------------------------------------------------------------
void BackupStoreFile::SetAESKey(const void *pKey, int KeyLength)
{
// Setup context
sAESEncrypt.Reset();
sAESEncrypt.Init(CipherContext::Encrypt, CipherAES(CipherDescription::Mode_CBC, pKey, KeyLength));
sAESDecrypt.Reset();
sAESDecrypt.Init(CipherContext::Decrypt, CipherAES(CipherDescription::Mode_CBC, pKey, KeyLength));
// Set encryption to use this key, instead of the "default" blowfish key
spEncrypt = &sAESEncrypt;
sEncryptCipherType = HEADER_AES_ENCODING;
}
#endif
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::MaxBlockSizeForChunkSize(int)
// Purpose: The maximum output size of a block, given the chunk size
// Created: 7/12/03
//
// --------------------------------------------------------------------------
int BackupStoreFile::MaxBlockSizeForChunkSize(int ChunkSize)
{
// Calculate... the maximum size of output by first the largest it could be after compression,
// which is encrypted, and has a 1 bytes header and the IV added, plus 1 byte for luck
// And then on top, add 128 bytes just to make sure. (Belts and braces approach to fixing
// an problem where a rather non-compressable file didn't fit in a block buffer.)
return sBlowfishEncrypt.MaxOutSizeForInBufferSize(Compress_MaxSizeForCompressedData(ChunkSize)) + 1 + 1
+ sBlowfishEncrypt.GetIVLength() + 128;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::EncodeChunk(const void *, int, BackupStoreFile::EncodingBuffer &)
// Purpose: Encodes a chunk (encryption, possible compressed beforehand)
// Created: 8/12/03
//
// --------------------------------------------------------------------------
int BackupStoreFile::EncodeChunk(const void *Chunk, int ChunkSize, BackupStoreFile::EncodingBuffer &rOutput)
{
ASSERT(spEncrypt != 0);
// Check there's some space in the output block
if(rOutput.mBufferSize < 256)
{
rOutput.Reallocate(256);
}
// Check alignment of the block
ASSERT((((uint32_t)(long)rOutput.mpBuffer) % BACKUPSTOREFILE_CODING_BLOCKSIZE) == BACKUPSTOREFILE_CODING_OFFSET);
// Want to compress it?
bool compressChunk = (ChunkSize >= BACKUP_FILE_MIN_COMPRESSED_CHUNK_SIZE);
// Build header
uint8_t header = sEncryptCipherType << HEADER_ENCODING_SHIFT;
if(compressChunk) header |= HEADER_CHUNK_IS_COMPRESSED;
// Store header
rOutput.mpBuffer[0] = header;
int outOffset = 1;
// Setup cipher, and store the IV
int ivLen = 0;
const void *iv = spEncrypt->SetRandomIV(ivLen);
::memcpy(rOutput.mpBuffer + outOffset, iv, ivLen);
outOffset += ivLen;
// Start encryption process
spEncrypt->Begin();
#define ENCODECHUNK_CHECK_SPACE(ToEncryptSize) \
{ \
if((rOutput.mBufferSize - outOffset) < ((ToEncryptSize) + 128)) \
{ \
rOutput.Reallocate(rOutput.mBufferSize + (ToEncryptSize) + 128); \
} \
}
// Encode the chunk
if(compressChunk)
{
// buffer to compress into
uint8_t buffer[2048];
// Set compressor with all the chunk as an input
Compress<true> compress;
compress.Input(Chunk, ChunkSize);
compress.FinishInput();
// Get and encrypt output
while(!compress.OutputHasFinished())
{
int s = compress.Output(buffer, sizeof(buffer));
if(s > 0)
{
ENCODECHUNK_CHECK_SPACE(s)
outOffset += spEncrypt->Transform(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset, buffer, s);
}
else
{
// Should never happen, as we put all the input in in one go.
// So if this happens, it means there's a logical problem somewhere
THROW_EXCEPTION(BackupStoreException, Internal)
}
}
ENCODECHUNK_CHECK_SPACE(16)
outOffset += spEncrypt->Final(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset);
}
else
{
// Straight encryption
ENCODECHUNK_CHECK_SPACE(ChunkSize)
outOffset += spEncrypt->Transform(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset, Chunk, ChunkSize);
ENCODECHUNK_CHECK_SPACE(16)
outOffset += spEncrypt->Final(rOutput.mpBuffer + outOffset, rOutput.mBufferSize - outOffset);
}
ASSERT(outOffset < rOutput.mBufferSize); // first check should have sorted this -- merely logic check
return outOffset;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::DecodeChunk(const void *, int, void *, int)
// Purpose: Decode an encoded chunk -- use OutputBufferSizeForKnownOutputSize() to find
// the extra output buffer size needed before calling.
// See notes in EncodeChunk() for notes re alignment of the
// encoded data.
// Created: 8/12/03
//
// --------------------------------------------------------------------------
int BackupStoreFile::DecodeChunk(const void *Encoded, int EncodedSize, void *Output, int OutputSize)
{
// Check alignment of the encoded block
ASSERT((((uint32_t)(long)Encoded) % BACKUPSTOREFILE_CODING_BLOCKSIZE) == BACKUPSTOREFILE_CODING_OFFSET);
// First check
if(EncodedSize < 1)
{
THROW_EXCEPTION(BackupStoreException, BadEncodedChunk)
}
const uint8_t *input = (uint8_t*)Encoded;
// Get header, make checks, etc
uint8_t header = input[0];
bool chunkCompressed = (header & HEADER_CHUNK_IS_COMPRESSED) == HEADER_CHUNK_IS_COMPRESSED;
uint8_t encodingType = (header >> HEADER_ENCODING_SHIFT);
if(encodingType != HEADER_BLOWFISH_ENCODING && encodingType != HEADER_AES_ENCODING)
{
THROW_EXCEPTION(BackupStoreException, ChunkHasUnknownEncoding)
}
#ifndef HAVE_OLD_SSL
// Choose cipher
CipherContext &cipher((encodingType == HEADER_AES_ENCODING)?sAESDecrypt:sBlowfishDecrypt);
#else
// AES not supported with this version of OpenSSL
if(encodingType == HEADER_AES_ENCODING)
{
THROW_EXCEPTION(BackupStoreException, AEScipherNotSupportedByInstalledOpenSSL)
}
CipherContext &cipher(sBlowfishDecrypt);
#endif
// Check enough space for header, an IV and one byte of input
int ivLen = cipher.GetIVLength();
if(EncodedSize < (1 + ivLen + 1))
{
THROW_EXCEPTION(BackupStoreException, BadEncodedChunk)
}
// Set IV in decrypt context, and start
cipher.SetIV(input + 1);
cipher.Begin();
// Setup vars for code
int inOffset = 1 + ivLen;
uint8_t *output = (uint8_t*)Output;
int outOffset = 0;
// Do action
if(chunkCompressed)
{
// Do things in chunks
uint8_t buffer[2048];
int inputBlockLen = cipher.InSizeForOutBufferSize(sizeof(buffer));
// Decompressor
Compress<false> decompress;
while(inOffset < EncodedSize)
{
// Decrypt a block
int bl = inputBlockLen;
if(bl > (EncodedSize - inOffset)) bl = EncodedSize - inOffset; // not too long
int s = cipher.Transform(buffer, sizeof(buffer), input + inOffset, bl);
inOffset += bl;
// Decompress the decrypted data
if(s > 0)
{
decompress.Input(buffer, s);
int os = 0;
do
{
os = decompress.Output(output + outOffset, OutputSize - outOffset);
outOffset += os;
} while(os > 0);
// Check that there's space left in the output buffer -- there always should be
if(outOffset >= OutputSize)
{
THROW_EXCEPTION(BackupStoreException, NotEnoughSpaceToDecodeChunk)
}
}
}
// Get any compressed data remaining in the cipher context and compression
int s = cipher.Final(buffer, sizeof(buffer));
decompress.Input(buffer, s);
decompress.FinishInput();
while(!decompress.OutputHasFinished())
{
int os = decompress.Output(output + outOffset, OutputSize - outOffset);
outOffset += os;
// Check that there's space left in the output buffer -- there always should be
if(outOffset >= OutputSize)
{
THROW_EXCEPTION(BackupStoreException, NotEnoughSpaceToDecodeChunk)
}
}
}
else
{
// Easy decryption
outOffset += cipher.Transform(output + outOffset, OutputSize - outOffset, input + inOffset, EncodedSize - inOffset);
outOffset += cipher.Final(output + outOffset, OutputSize - outOffset);
}
return outOffset;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::ReorderFileToStreamOrder(IOStream *, bool)
// Purpose: Returns a stream which gives a Stream order version of the encoded file.
// If TakeOwnership == true, then the input stream will be deleted when the
// returned stream is deleted.
// The input stream must be seekable.
// Created: 10/12/03
//
// --------------------------------------------------------------------------
std::auto_ptr<IOStream> BackupStoreFile::ReorderFileToStreamOrder(IOStream *pStream, bool TakeOwnership)
{
ASSERT(pStream != 0);
// Get the size of the file
int64_t fileSize = pStream->BytesLeftToRead();
if(fileSize == IOStream::SizeOfStreamUnknown)
{
THROW_EXCEPTION(BackupStoreException, StreamDoesntHaveRequiredFeatures)
}
// Read the header
int bytesRead = 0;
file_StreamFormat hdr;
bool readBlock = pStream->ReadFullBuffer(&hdr, sizeof(hdr), &bytesRead);
// Seek backwards to put the file pointer back where it was before we started this
pStream->Seek(0 - bytesRead, IOStream::SeekType_Relative);
// Check we got a block
if(!readBlock)
{
// Couldn't read header -- assume file bad
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// Check magic number
if(ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V1
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
&& ntohl(hdr.mMagicValue) != OBJECTMAGIC_FILE_MAGIC_VALUE_V0
#endif
)
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// Get number of blocks
int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
// Calculate where the block index will be, check it's reasonable
int64_t blockIndexSize = ((numBlocks * sizeof(file_BlockIndexEntry)) + sizeof(file_BlockIndexHeader));
int64_t blockIndexLoc = fileSize - blockIndexSize;
if(blockIndexLoc < 0)
{
// Doesn't look good!
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
// Build a reordered stream
std::auto_ptr<IOStream> reordered(new ReadGatherStream(TakeOwnership));
// Set it up...
ReadGatherStream &rreordered(*((ReadGatherStream*)reordered.get()));
int component = rreordered.AddComponent(pStream);
// Send out the block index
rreordered.AddBlock(component, blockIndexSize, true, blockIndexLoc);
// And then the rest of the file
rreordered.AddBlock(component, blockIndexLoc, true, 0);
return reordered;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::ResetStats()
// Purpose: Reset the gathered statistics
// Created: 20/1/04
//
// --------------------------------------------------------------------------
void BackupStoreFile::ResetStats()
{
msStats.mBytesInEncodedFiles = 0;
msStats.mBytesAlreadyOnServer = 0;
msStats.mTotalFileStreamSize = 0;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::CompareFileContentsAgainstBlockIndex(const char *, IOStream &)
// Purpose: Compares the contents of a file against the checksums contained in the
// block index. Returns true if the checksums match, meaning the file is
// extremely likely to match the original. Will always consume the entire index.
// Created: 21/1/04
//
// --------------------------------------------------------------------------
bool BackupStoreFile::CompareFileContentsAgainstBlockIndex(const char *Filename, IOStream &rBlockIndex, int Timeout)
{
// is it a symlink?
bool sourceIsSymlink = false;
{
struct stat st;
if(::lstat(Filename, &st) == -1)
{
THROW_EXCEPTION(CommonException, OSFileError)
}
if((st.st_mode & S_IFMT) == S_IFLNK)
{
sourceIsSymlink = true;
}
}
// Open file, if it's not a symlink
std::auto_ptr<FileStream> in;
if(!sourceIsSymlink)
{
in.reset(new FileStream(Filename));
}
// Read header
file_BlockIndexHeader hdr;
if(!rBlockIndex.ReadFullBuffer(&hdr, sizeof(hdr), 0 /* not interested in bytes read if this fails */, Timeout))
{
// Couldn't read header
THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
}
// Check magic
if(hdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V1)
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
&& hdr.mMagicValue != (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0)
#endif
)
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
bool isOldVersion = hdr.mMagicValue == (int32_t)htonl(OBJECTMAGIC_FILE_BLOCKS_MAGIC_VALUE_V0);
#endif
// Get basic information
int64_t numBlocks = box_ntoh64(hdr.mNumBlocks);
uint64_t entryIVBase = box_ntoh64(hdr.mEntryIVBase);
//TODO: Verify that these sizes look reasonable
// setup
void *data = 0;
int32_t dataSize = -1;
bool matches = true;
int64_t totalSizeInBlockIndex = 0;
try
{
for(int64_t b = 0; b < numBlocks; ++b)
{
// Read an entry from the stream
file_BlockIndexEntry entry;
if(!rBlockIndex.ReadFullBuffer(&entry, sizeof(entry), 0 /* not interested in bytes read if this fails */, Timeout))
{
// Couldn't read entry
THROW_EXCEPTION(BackupStoreException, CouldntReadEntireStructureFromStream)
}
// Calculate IV for this entry
uint64_t iv = entryIVBase;
iv += b;
iv = box_hton64(iv);
#ifndef BOX_DISABLE_BACKWARDS_COMPATIBILITY_BACKUPSTOREFILE
if(isOldVersion)
{
// Reverse the IV for compatibility
iv = box_swap64(iv);
}
#endif
sBlowfishDecryptBlockEntry.SetIV(&iv);
// Decrypt the encrypted section
file_BlockIndexEntryEnc entryEnc;
int sectionSize = sBlowfishDecryptBlockEntry.TransformBlock(&entryEnc, sizeof(entryEnc),
entry.mEnEnc, sizeof(entry.mEnEnc));
if(sectionSize != sizeof(entryEnc))
{
THROW_EXCEPTION(BackupStoreException, BlockEntryEncodingDidntGiveExpectedLength)
}
// Size of block
int32_t blockClearSize = ntohl(entryEnc.mSize);
if(blockClearSize < 0 || blockClearSize > (BACKUP_FILE_MAX_BLOCK_SIZE + 1024))
{
THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile)
}
totalSizeInBlockIndex += blockClearSize;
// Make sure there's enough memory allocated to load the block in
if(dataSize < blockClearSize)
{
// Too small, free the block if it's already allocated
if(data != 0)
{
::free(data);
data = 0;
}
// Allocate a block
data = ::malloc(blockClearSize + 128);
if(data == 0)
{
throw std::bad_alloc();
}
dataSize = blockClearSize + 128;
}
// Load in the block from the file, if it's not a symlink
if(!sourceIsSymlink)
{
if(in->Read(data, blockClearSize) != blockClearSize)
{
// Not enough data left in the file, can't possibly match
matches = false;
}
else
{
// Check the checksum
MD5Digest md5;
md5.Add(data, blockClearSize);
md5.Finish();
if(!md5.DigestMatches(entryEnc.mStrongChecksum))
{
// Checksum didn't match
matches = false;
}
}
}
// Keep on going regardless, to make sure the entire block index stream is read
// -- must always be consistent about what happens with the stream.
}
}
catch(...)
{
// clean up in case of errors
if(data != 0)
{
::free(data);
data = 0;
}
throw;
}
// free block
if(data != 0)
{
::free(data);
data = 0;
}
// Check for data left over if it's not a symlink
if(!sourceIsSymlink)
{
// Anything left to read in the file?
if(in->BytesLeftToRead() != 0)
{
// File has extra data at the end
matches = false;
}
}
// Symlinks must have zero size on server
if(sourceIsSymlink)
{
matches = (totalSizeInBlockIndex == 0);
}
return matches;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::EncodingBuffer::EncodingBuffer()
// Purpose: Constructor
// Created: 25/11/04
//
// --------------------------------------------------------------------------
BackupStoreFile::EncodingBuffer::EncodingBuffer()
: mpBuffer(0),
mBufferSize(0)
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::EncodingBuffer::~EncodingBuffer()
// Purpose: Destructor
// Created: 25/11/04
//
// --------------------------------------------------------------------------
BackupStoreFile::EncodingBuffer::~EncodingBuffer()
{
if(mpBuffer != 0)
{
BackupStoreFile::CodingChunkFree(mpBuffer);
mpBuffer = 0;
}
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::EncodingBuffer::Allocate(int)
// Purpose: Do initial allocation of block
// Created: 25/11/04
//
// --------------------------------------------------------------------------
void BackupStoreFile::EncodingBuffer::Allocate(int Size)
{
ASSERT(mpBuffer == 0);
uint8_t *buffer = (uint8_t*)BackupStoreFile::CodingChunkAlloc(Size);
if(buffer == 0)
{
throw std::bad_alloc();
}
mpBuffer = buffer;
mBufferSize = Size;
}
// --------------------------------------------------------------------------
//
// Function
// Name: BackupStoreFile::EncodingBuffer::Reallocate(int)
// Purpose: Reallocate the block. Try not to call this, it has to copy
// the entire contents as the block can't be reallocated straight.
// Created: 25/11/04
//
// --------------------------------------------------------------------------
void BackupStoreFile::EncodingBuffer::Reallocate(int NewSize)
{
#ifndef WIN32
TRACE2("Reallocating EncodingBuffer from %d to %d\n", mBufferSize, NewSize);
#endif
ASSERT(mpBuffer != 0);
uint8_t *buffer = (uint8_t*)BackupStoreFile::CodingChunkAlloc(NewSize);
if(buffer == 0)
{
throw std::bad_alloc();
}
// Copy data
::memcpy(buffer, mpBuffer, (NewSize > mBufferSize)?mBufferSize:NewSize);
// Free old
BackupStoreFile::CodingChunkFree(mpBuffer);
// Store new buffer
mpBuffer = buffer;
mBufferSize = NewSize;
}
// --------------------------------------------------------------------------
//
// Function
// Name: DiffTimer::DiffTimer();
// Purpose: Constructor
// Created: 2005/02/01
//
// --------------------------------------------------------------------------
DiffTimer::DiffTimer()
{
}
// --------------------------------------------------------------------------
//
// Function
// Name: DiffTimer::DiffTimer();
// Purpose: Destructor
// Created: 2005/02/01
//
// --------------------------------------------------------------------------
DiffTimer::~DiffTimer()
{
}
syntax highlighted by Code2HTML, v. 0.9.1