1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
// Copyright (c) 2012-2014 Konstantin Isakov <ikm@zbackup.org> and ZBackup contributors, see CONTRIBUTORS
// Part of ZBackup. Licensed under GNU GPLv2 or later + OpenSSL, see LICENSE
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include "../../encrypted_file.hh"
#include "../../encryption_key.hh"
#include "../../random.hh"
#include "../../tmp_mgr.hh"
#include "../../check.hh"
#include "../../adler32.hh"
#include "../../bundle.hh"
#include "../../compression.hh"
#include "../../message.hh"
using namespace Compression;
char tmpbuf[100];
void testCompatibility()
{
// The LZO code uses a different file header than the previous code
// because it adds the compression_method field. Nevertheless, it
// must be compatible with previous code.
TmpMgr tmpMgr( "/dev/shm" );
sptr< TemporaryFile > tempFile = tmpMgr.makeTemporaryFile();
std::string fileName = tempFile->getFileName();
EncryptionKey noKey( std::string(), NULL );
// Write old header, read as new header
{
{
EncryptedFile::OutputStream os( fileName.c_str(), noKey, Encryption::ZeroIv );
FileHeader header;
header.set_version( 42 );
Message::serialize( header, os );
}
{
EncryptedFile::InputStream is( fileName.c_str(), noKey, Encryption::ZeroIv );
BundleFileHeader header;
Message::parse( header, is );
CHECK( header.version() == 42, "version is wrong when reading old header with new program" );
CHECK( header.compression_method() == "lzma", "compression_method is wrong when reading old header with new program" );
}
}
// Write new header, read as old header
//NOTE In the real code, this will only work, if the file uses LZMA. If it doesn't, the version
// field is increased and the old code will refuse to read the file.
{
{
EncryptedFile::OutputStream os( fileName.c_str(), noKey, Encryption::ZeroIv );
BundleFileHeader header;
header.set_version( 42 );
Message::serialize( header, os );
}
{
EncryptedFile::InputStream is( fileName.c_str(), noKey, Encryption::ZeroIv );
FileHeader header;
Message::parse( header, is );
CHECK( header.version() == 42, "version is wrong when reading new header with old program" );
// cannot check compression_method because the field doesn't exist
}
}
printf("compatibility test successful.\n");
}
void readAndWrite( EncryptionKey const & key,
const_sptr<CompressionMethod> compression1, const_sptr<CompressionMethod> compression2 )
{
// temporary file for the bundle
TmpMgr tmpMgr( "/dev/shm" );
sptr< TemporaryFile > tempFile = tmpMgr.makeTemporaryFile();
// some chunk data
int chunkCount = rand() % 30;
size_t chunkSize = rand() % 20 ? 64*1024 : 10;
char** chunks = new char*[chunkCount];
string* chunkIds = new string[chunkCount];
CompressionMethod::defaultCompression = compression1;
// write bundle
{
Bundle::Creator bundle;
for (int i=0;i<chunkCount;i++) {
chunks[i] = new char[chunkSize];
Random::genaratePseudo( chunks[i], chunkSize );
//TODO make it look like a real Id (or even let it match the data)
//TODO make sure we don't have any duplicate Ids
sprintf(tmpbuf, "0x%08x", rand());
chunkIds[i] = string(tmpbuf);
bundle.addChunk( chunkIds[i], chunks[i], chunkSize );
}
bundle.write( tempFile->getFileName().c_str(), key );
}
CompressionMethod::defaultCompression = compression2;
// read it and compare
{
Bundle::Reader bundle( tempFile->getFileName().c_str(), key );
for (int i=0;i<chunkCount;i++) {
string data;
size_t size;
bool ret = bundle.get( chunkIds[i], data, size );
CHECK( ret, "bundle.get returned false for chunk %d (%s)", i, chunkIds[i].c_str() );
CHECK( size == chunkSize, "wrong chunk size for chunk %d (%s)", i, chunkIds[i].c_str() );
CHECK( memcmp(data.c_str(), chunks[i], chunkSize) == 0, "wrong chunk data for chunk %d (%s)", i, chunkIds[i].c_str() );
}
}
// clean up
for (int i=0;i<chunkCount;i++)
delete[] chunks[i];
delete[] chunks;
//TODO does that call the destructors?
delete[] chunkIds;
printf(".");
fflush(stdout);
}
int main()
{
EncryptionKeyInfo keyInfo;
EncryptionKey::generate( "blah", keyInfo );
EncryptionKey key( "blah", &keyInfo );
EncryptionKey noKey( std::string(), NULL );
testCompatibility();
std::vector< const_sptr<CompressionMethod> > compressions;
for ( CompressionMethod::iterator it = CompressionMethod::begin(); it!=CompressionMethod::end(); ++it ) {
printf( "supported compression: %s\n", (*it)->getName().c_str() );
compressions.push_back( *it );
}
for ( size_t iteration = 100; iteration--; ) {
// default compression while writing the file
const_sptr<CompressionMethod> compression1 = compressions[ rand() % compressions.size() ];
// default compression while reading the file
// The reader should ignore it and always use the compression that was used for the file.
const_sptr<CompressionMethod> compression2 = compressions[ rand() % compressions.size() ];
readAndWrite( ( rand() & 1 ) ? key : noKey, compression1, compression2 );
}
printf("\n");
return 0;
}
|