Added support for -o lzma.compression_level:

* Closes https://github.com/zbackup/zbackup/pull/10 completely
* editConfigInteractively moved to ZBackupBase again (to make possible
  to use Config almost anywhere)
master
Vladimir Stackov 2015-01-23 14:32:40 +03:00
parent c5b821dba4
commit 30fe01a2a0
12 changed files with 151 additions and 70 deletions

View File

@ -90,7 +90,8 @@ void Creator::write( std::string const & fileName, EncryptionKey const & key,
} }
} }
void Creator::write( std::string const & fileName, EncryptionKey const & key ) void Creator::write( Config const & config, std::string const & fileName,
EncryptionKey const & key )
{ {
EncryptedFile::OutputStream os( fileName.c_str(), key, Encryption::ZeroIv ); EncryptedFile::OutputStream os( fileName.c_str(), key, Encryption::ZeroIv );
@ -98,7 +99,8 @@ void Creator::write( std::string const & fileName, EncryptionKey const & key )
BundleFileHeader header; BundleFileHeader header;
const_sptr<Compression::CompressionMethod> compression = Compression::CompressionMethod::selectedCompression; const_sptr<Compression::CompressionMethod> compression =
Compression::CompressionMethod::selectedCompression;
header.set_compression_method( compression->getName() ); header.set_compression_method( compression->getName() );
// The old code only support lzma, so we will bump up the version, if we're // The old code only support lzma, so we will bump up the version, if we're
@ -115,7 +117,8 @@ void Creator::write( std::string const & fileName, EncryptionKey const & key )
// Compress // Compress
sptr<Compression::EnDecoder> encoder = compression->createEncoder(); sptr<Compression::EnDecoder> encoder = compression->createEncoder(
config );
encoder->setInput( payload.data(), payload.size() ); encoder->setInput( payload.data(), payload.size() );
@ -135,7 +138,7 @@ void Creator::write( std::string const & fileName, EncryptionKey const & key )
} }
// Perform the compression // Perform the compression
if ( encoder->process(true) ) if ( encoder->process( true ) )
{ {
if ( encoder->getAvailableOutput() ) if ( encoder->getAvailableOutput() )
os.BackUp( encoder->getAvailableOutput() ); os.BackUp( encoder->getAvailableOutput() );
@ -190,7 +193,8 @@ Reader::Reader( string const & fileName, EncryptionKey const & key, bool prohibi
decoder->setInput( data, size ); decoder->setInput( data, size );
} }
if ( decoder->process(false) ) { if ( decoder->process( false ) )
{
if ( decoder->getAvailableInput() ) if ( decoder->getAvailableInput() )
is.BackUp( decoder->getAvailableInput() ); is.BackUp( decoder->getAvailableInput() );
break; break;

View File

@ -17,6 +17,7 @@
#include "static_assert.hh" #include "static_assert.hh"
#include "zbackup.pb.h" #include "zbackup.pb.h"
#include "encrypted_file.hh" #include "encrypted_file.hh"
#include "config.hh"
namespace Bundle { namespace Bundle {
@ -101,7 +102,7 @@ public:
/// Compresses and writes the bundle to the given file. The operation is /// Compresses and writes the bundle to the given file. The operation is
/// time-consuming - calling this function from a worker thread could be /// time-consuming - calling this function from a worker thread could be
/// warranted /// warranted
void write( string const & fileName, EncryptionKey const & ); void write( Config const &, string const & fileName, EncryptionKey const & );
void write( string const & fileName, EncryptionKey const &, void write( string const & fileName, EncryptionKey const &,
Bundle::Reader & reader ); Bundle::Reader & reader );

View File

@ -130,7 +130,8 @@ void Writer::finishCurrentBundle()
while ( runningCompressors >= maxCompressorsToRun ) while ( runningCompressors >= maxCompressorsToRun )
runningCompressorsCondition.wait( runningCompressorsMutex ); runningCompressorsCondition.wait( runningCompressorsMutex );
Compressor * compressor = new Compressor( *this, currentBundle, Compressor * compressor = new Compressor( config,
*this, currentBundle,
file->getFileName() ); file->getFileName() );
currentBundle.reset(); currentBundle.reset();
@ -159,10 +160,11 @@ Bundle::Id const & Writer::getCurrentBundleId()
return currentBundleId; return currentBundleId;
} }
Writer::Compressor::Compressor( Writer & writer, Writer::Compressor::Compressor( Config const & configIn, Writer & writer,
sptr< Bundle::Creator > const & bundleCreator, sptr< Bundle::Creator > const & bundleCreator,
string const & fileName ): string const & fileName ):
writer( writer ), bundleCreator( bundleCreator ), fileName( fileName ) writer( writer ), bundleCreator( bundleCreator ), fileName( fileName ),
config( configIn )
{ {
} }
@ -170,7 +172,7 @@ void * Writer::Compressor::Compressor::threadFunction() throw()
{ {
try try
{ {
bundleCreator->write( fileName, writer.encryptionKey ); bundleCreator->write( config, fileName, writer.encryptionKey );
} }
catch( std::exception & e ) catch( std::exception & e )
{ {

View File

@ -68,8 +68,9 @@ private:
Writer & writer; Writer & writer;
sptr< Bundle::Creator > bundleCreator; sptr< Bundle::Creator > bundleCreator;
string fileName; string fileName;
Config const & config;
public: public:
Compressor( Writer &, sptr< Bundle::Creator > const &, Compressor( Config const &, Writer &, sptr< Bundle::Creator > const &,
string const & fileName ); string const & fileName );
protected: protected:
virtual void * threadFunction() throw(); virtual void * threadFunction() throw();

View File

@ -8,11 +8,17 @@
namespace Compression { namespace Compression {
EnDecoder::EnDecoder() { } EnDecoder::EnDecoder()
EnDecoder::~EnDecoder() { } {
}
CompressionMethod::~CompressionMethod() { } EnDecoder::~EnDecoder()
{
}
CompressionMethod::~CompressionMethod()
{
}
// LZMA // LZMA
@ -72,8 +78,17 @@ class LZMAEncoder : public LZMAEnDecoder
public: public:
LZMAEncoder() LZMAEncoder()
{ {
uint32_t preset = 6; // TODO: make this customizable, although 6 seems to be uint32_t preset = 6;
// the best option lzma_ret ret = lzma_easy_encoder( &strm, preset, LZMA_CHECK_CRC64 );
CHECK( ret == LZMA_OK, "lzma_easy_encoder error: %d", (int) ret );
}
LZMAEncoder( Config const & config )
{
uint32_t compressionLevel = config.GET_STORABLE( lzma, compression_level );
uint32_t preset = ( compressionLevel > 9 ) ?
( compressionLevel - 10 ) | LZMA_PRESET_EXTREME :
compressionLevel;
lzma_ret ret = lzma_easy_encoder( &strm, preset, LZMA_CHECK_CRC64 ); lzma_ret ret = lzma_easy_encoder( &strm, preset, LZMA_CHECK_CRC64 );
CHECK( ret == LZMA_OK, "lzma_easy_encoder error: %d", (int) ret ); CHECK( ret == LZMA_OK, "lzma_easy_encoder error: %d", (int) ret );
} }
@ -92,6 +107,11 @@ public:
class LZMACompression : public CompressionMethod class LZMACompression : public CompressionMethod
{ {
public: public:
sptr<EnDecoder> createEncoder( Config const & config ) const
{
return new LZMAEncoder( config );
}
sptr<EnDecoder> createEncoder() const sptr<EnDecoder> createEncoder() const
{ {
return new LZMAEncoder(); return new LZMAEncoder();
@ -105,7 +125,6 @@ public:
std::string getName() const { return "lzma"; } std::string getName() const { return "lzma"; }
}; };
// LZO // LZO
// liblzo implements a lot of algorithms "for unlimited backward compatibility" // liblzo implements a lot of algorithms "for unlimited backward compatibility"
@ -232,7 +251,7 @@ public:
{ {
// data has been encoded or decoded, remaining output is in accDataOut // data has been encoded or decoded, remaining output is in accDataOut
// -> copy to output // -> copy to output
if (availOut > 0 && accDataOut.size() - posInAccDataOut > 0) if ( availOut > 0 && accDataOut.size() - posInAccDataOut > 0 )
{ {
size_t sz = availOut; size_t sz = availOut;
if ( sz > accDataOut.size() - posInAccDataOut ) if ( sz > accDataOut.size() - posInAccDataOut )
@ -273,7 +292,7 @@ private:
// we use our own buffer // we use our own buffer
size_t bufferSize = suggestOutputSize( dataIn, availIn ); size_t bufferSize = suggestOutputSize( dataIn, availIn );
do { do {
accDataOut.resize(bufferSize); accDataOut.resize( bufferSize );
size_t outputSize; size_t outputSize;
//TODO doc says we mustn't modify the pointer returned by data()... //TODO doc says we mustn't modify the pointer returned by data()...
@ -312,7 +331,6 @@ protected:
virtual bool doProcessNoSize( const char* dataIn, size_t availIn, virtual bool doProcessNoSize( const char* dataIn, size_t availIn,
char* dataOut, size_t availOut, size_t& outputSize ) =0; char* dataOut, size_t availOut, size_t& outputSize ) =0;
bool shouldTryWith( const char* dataIn, size_t availIn, size_t availOut ) bool shouldTryWith( const char* dataIn, size_t availIn, size_t availOut )
{ {
return suggestOutputSize( dataIn, availIn ) <= availOut; return suggestOutputSize( dataIn, availIn ) <= availOut;
@ -388,7 +406,6 @@ protected:
virtual bool doProcessNoSize( const char* dataIn, size_t availIn, virtual bool doProcessNoSize( const char* dataIn, size_t availIn,
char* dataOut, size_t availOut, size_t& outputSize ) =0; char* dataOut, size_t availOut, size_t& outputSize ) =0;
bool shouldTryWith( const char*, size_t, size_t availOut ) bool shouldTryWith( const char*, size_t, size_t availOut )
{ {
// If the compression doesn't use any spaces... // If the compression doesn't use any spaces...
@ -443,7 +460,6 @@ protected:
} }
}; };
#ifdef HAVE_LIBLZO #ifdef HAVE_LIBLZO
#include <lzo/lzo1x.h> #include <lzo/lzo1x.h>
@ -472,9 +488,9 @@ class LZO1X_1_Compression;
class LZO1X_1_Encoder : public NoStreamAndUnknownSizeEncoder class LZO1X_1_Encoder : public NoStreamAndUnknownSizeEncoder
{ {
const LZO1X_1_Compression* compression; const LZO1X_1_Compression* compression;
static size_t calcMaxCompressedSize(size_t availIn); static size_t calcMaxCompressedSize( size_t availIn );
public: public:
LZO1X_1_Encoder(const LZO1X_1_Compression* compression) LZO1X_1_Encoder( const LZO1X_1_Compression* compression )
{ {
this->compression = compression; this->compression = compression;
} }
@ -499,13 +515,19 @@ class LZO1X_1_Compression : public CompressionMethod
} }
} }
public: public:
sptr<EnDecoder> createEncoder() const sptr< EnDecoder > createEncoder( Config const & config ) const
{ {
init(); init();
return new LZO1X_1_Encoder(this); return new LZO1X_1_Encoder(this);
} }
sptr<EnDecoder> createDecoder() const sptr< EnDecoder > createEncoder() const
{
init();
return new LZO1X_1_Encoder(this);
}
sptr< EnDecoder > createDecoder() const
{ {
init(); init();
return new LZO1X_1_Decoder(); return new LZO1X_1_Decoder();
@ -513,7 +535,6 @@ public:
std::string getName() const { return "lzo1x_1"; } std::string getName() const { return "lzo1x_1"; }
lzo_voidp getWorkmem( size_t size ) const lzo_voidp getWorkmem( size_t size ) const
{ {
return new char[size]; return new char[size];
@ -531,7 +552,6 @@ public:
bool LZO1X_1_Compression::initialized = false; bool LZO1X_1_Compression::initialized = false;
size_t LZO1X_1_Encoder::calcMaxCompressedSize( size_t availIn ) size_t LZO1X_1_Encoder::calcMaxCompressedSize( size_t availIn )
{ {
// It seems that lzo1x_1_compress does NOT check whether the buffer is big enough. // It seems that lzo1x_1_compress does NOT check whether the buffer is big enough.
@ -596,12 +616,12 @@ const_sptr< CompressionMethod > const CompressionMethod::compressions[] = {
}; };
const_sptr< CompressionMethod > CompressionMethod::selectedCompression = const_sptr< CompressionMethod > CompressionMethod::selectedCompression =
compressions[0]; compressions[ 0 ];
const_sptr< CompressionMethod > CompressionMethod::findCompression( const_sptr< CompressionMethod > CompressionMethod::findCompression(
const std::string& name, bool optional ) const std::string& name, bool optional )
{ {
for ( const const_sptr<CompressionMethod>* c = compressions+0; *c; ++c ) for ( const const_sptr<CompressionMethod>* c = compressions + 0; *c; ++c )
{ {
if ( (*c)->getName() == name ) if ( (*c)->getName() == name )
{ {
@ -616,9 +636,15 @@ const_sptr< CompressionMethod > CompressionMethod::findCompression(
} }
// iterator over compressions // iterator over compressions
CompressionMethod::iterator::iterator( const const_sptr< CompressionMethod > * ptr ):
ptr( ptr )
{
}
CompressionMethod::iterator::iterator( const const_sptr<CompressionMethod>* ptr ) : ptr( ptr) { } CompressionMethod::iterator::iterator( const iterator & it ):
CompressionMethod::iterator::iterator( const iterator& it ) : ptr(it.ptr) { } ptr( it.ptr )
{
}
CompressionMethod::iterator& CompressionMethod::iterator::operator =( const iterator& it ) CompressionMethod::iterator& CompressionMethod::iterator::operator =( const iterator& it )
{ {

View File

@ -7,6 +7,7 @@
#include "sptr.hh" #include "sptr.hh"
#include "ex.hh" #include "ex.hh"
#include "nocopy.hh" #include "nocopy.hh"
#include "config.hh"
namespace Compression { namespace Compression {
@ -48,6 +49,7 @@ public:
// This name is saved in the file header of the compressed file. // This name is saved in the file header of the compressed file.
virtual std::string getName() const = 0; virtual std::string getName() const = 0;
virtual sptr< EnDecoder > createEncoder( Config const & ) const = 0;
virtual sptr< EnDecoder > createEncoder() const = 0; virtual sptr< EnDecoder > createEncoder() const = 0;
virtual sptr< EnDecoder > createDecoder() const = 0; virtual sptr< EnDecoder > createDecoder() const = 0;

View File

@ -22,6 +22,9 @@
return true; \ return true; \
} }
// Some configurables could be just a switch
// So we introducing a macros that would indicate
// that this configurable is not a switch
#define REQUIRE_VALUE \ #define REQUIRE_VALUE \
{ \ { \
if ( !hasValue && !validate ) \ if ( !hasValue && !validate ) \
@ -52,7 +55,9 @@ static struct
Config::oChunk_max_size, Config::oChunk_max_size,
Config::Storable, Config::Storable,
"Maximum chunk size used when storing chunks\n" "Maximum chunk size used when storing chunks\n"
"Affects deduplication ratio directly" "Affects deduplication ratio directly\n"
"Default is %s",
Utils::numberToString( defaultConfig.GET_STORABLE( chunk, max_size ) )
}, },
{ {
"bundle.max_payload_size", "bundle.max_payload_size",
@ -61,13 +66,26 @@ static struct
"Maximum number of bytes a bundle can hold. Only real chunk bytes are\n" "Maximum number of bytes a bundle can hold. Only real chunk bytes are\n"
"counted, not metadata. Any bundle should be able to contain at least\n" "counted, not metadata. Any bundle should be able to contain at least\n"
"one arbitrary single chunk, so this should not be smaller than\n" "one arbitrary single chunk, so this should not be smaller than\n"
"chunk.max_size" "chunk.max_size\n"
"Default is %s",
Utils::numberToString( defaultConfig.GET_STORABLE( bundle, max_payload_size ) )
}, },
{ {
"bundle.compression_method", "bundle.compression_method",
Config::oBundle_compression_method, Config::oBundle_compression_method,
Config::Storable, Config::Storable,
"Compression method for new bundles" "Compression method for new bundles\n"
"Default is %s",
defaultConfig.GET_STORABLE( bundle, compression_method )
},
{
"lzma.compression_level",
Config::oLZMA_compression_level,
Config::Storable,
"Compression level for new LZMA-compressed files\n"
"Valid values: 0-19 (values over 9 enables extreme mode)\n"
"Default is %s",
Utils::numberToString( defaultConfig.GET_STORABLE( lzma, compression_level ) )
}, },
// Shortcuts for storable options // Shortcuts for storable options
@ -75,7 +93,9 @@ static struct
"compression", "compression",
Config::oBundle_compression_method, Config::oBundle_compression_method,
Config::Storable, Config::Storable,
"Shortcut for bundle.compression_method" "Shortcut for bundle.compression_method\n"
"Default is %s",
defaultConfig.GET_STORABLE( bundle, compression_method )
}, },
// Runtime options // Runtime options
@ -239,6 +259,25 @@ bool Config::parseOrValidate( const char * option, const OptionType type,
/* NOTREACHED */ /* NOTREACHED */
break; break;
case oLZMA_compression_level:
REQUIRE_VALUE;
if ( PARSE_OR_VALIDATE(
sscanf( optionValue, "%zu %n", &uint32Value, &n ) != 1 ||
optionValue[ n ] || uint32Value > 19,
GET_STORABLE( lzma, compression_level ) > 19 )
)
return false;
SKIP_ON_VALIDATION;
SET_STORABLE( lzma, compression_level, uint32Value );
dPrintf( "storable[lzma][compression_level] = %zu\n",
GET_STORABLE( lzma, compression_level ) );
return true;
/* NOTREACHED */
break;
case oBundle_compression_method: case oBundle_compression_method:
REQUIRE_VALUE; REQUIRE_VALUE;
@ -504,6 +543,7 @@ void Config::reset_storable()
SET_STORABLE( chunk, max_size, GET_STORABLE( chunk, max_size ) ); SET_STORABLE( chunk, max_size, GET_STORABLE( chunk, max_size ) );
SET_STORABLE( bundle, max_payload_size, GET_STORABLE( bundle, max_payload_size ) ); SET_STORABLE( bundle, max_payload_size, GET_STORABLE( bundle, max_payload_size ) );
SET_STORABLE( bundle, compression_method, GET_STORABLE( bundle, compression_method ) ); SET_STORABLE( bundle, compression_method, GET_STORABLE( bundle, compression_method ) );
SET_STORABLE( lzma, compression_level, GET_STORABLE( lzma, compression_level ) );
} }
void Config::show() void Config::show()
@ -515,27 +555,3 @@ void Config::show( const ConfigInfo & config )
{ {
printf( "%s", toString( config ).c_str() ); printf( "%s", toString( config ).c_str() );
} }
bool Config::editInteractively( ZBackupBase * zbb )
{
string configData( toString( *zbb->config.storable ) );
if ( !zbb->spawnEditor( configData, &validateProto ) )
return false;
ConfigInfo newConfig;
parseProto( configData, &newConfig );
if ( toString( *zbb->config.storable ) == toString( newConfig ) )
{
verbosePrintf( "No changes made to config\n" );
return false;
}
verbosePrintf( "Updating configuration...\n" );
zbb->config.storable->MergeFrom( newConfig );
verbosePrintf(
"Configuration successfully updated!\n"
"Updated configuration:\n%s", toString( *zbb->config.storable ).c_str() );
return true;
}

View File

@ -21,8 +21,6 @@
using std::string; using std::string;
using std::bitset; using std::bitset;
class ZBackupBase;
class Config class Config
{ {
public: public:
@ -55,6 +53,7 @@ public:
oChunk_max_size, oChunk_max_size,
oBundle_max_payload_size, oBundle_max_payload_size,
oBundle_compression_method, oBundle_compression_method,
oLZMA_compression_level,
oRuntime_threads, oRuntime_threads,
oRuntime_cacheSize, oRuntime_cacheSize,
@ -72,10 +71,6 @@ public:
static string toString( google::protobuf::Message const & ); static string toString( google::protobuf::Message const & );
// Edit current configuration
// returns true if configuration is changed
static bool editInteractively( ZBackupBase * );
// Print configuration to screen // Print configuration to screen
static void show( const ConfigInfo & ); static void show( const ConfigInfo & );
void show(); void show();
@ -96,6 +91,4 @@ private:
bool want_cleanup; bool want_cleanup;
}; };
#include "zbackup_base.hh"
#endif #endif

View File

@ -649,7 +649,7 @@ invalid_option:
{ {
ZBackupBase zbb( ZBackupBase::deriveStorageDirFromBackupsFile( args[ fieldStorage ], true ), ZBackupBase zbb( ZBackupBase::deriveStorageDirFromBackupsFile( args[ fieldStorage ], true ),
passwords[ 0 ], true ); passwords[ 0 ], true );
if ( Config::editInteractively( &zbb ) ) if ( zbb.editConfigInteractively() )
zbb.saveExtendedStorageInfo(); zbb.saveExtendedStorageInfo();
} }
else else

View File

@ -42,6 +42,12 @@ message StorageInfo
optional string default_compression_method = 4 [default = "lzma", deprecated = true]; optional string default_compression_method = 4 [default = "lzma", deprecated = true];
} }
message LZMAConfigInfo
{
// Compression level for new LZMA-compressed files
optional uint32 compression_level = 1 [default = 6];
}
message ChunkConfigInfo message ChunkConfigInfo
{ {
// Maximum chunk size used when storing chunks // Maximum chunk size used when storing chunks
@ -64,6 +70,7 @@ message ConfigInfo
{ {
required ChunkConfigInfo chunk = 1; required ChunkConfigInfo chunk = 1;
required BundleConfigInfo bundle = 2; required BundleConfigInfo bundle = 2;
required LZMAConfigInfo lzma = 3;
} }
message ExtendedStorageInfo message ExtendedStorageInfo

View File

@ -116,7 +116,7 @@ ZBackupBase::ZBackupBase( string const & storageDir, string const & password,
storageDir.c_str() ); storageDir.c_str() );
} }
// Update all internal variables after configuration change // Update all internal variables according to real configuration
// Dunno why someone need to store duplicate information // Dunno why someone need to store duplicate information
// in deduplication utility // in deduplication utility
void ZBackupBase::propagateUpdate() void ZBackupBase::propagateUpdate()
@ -419,3 +419,28 @@ end:
return isChanged; return isChanged;
} }
bool ZBackupBase::editConfigInteractively()
{
string configData( Config::toString( *config.storable ) );
if ( !spawnEditor( configData, &Config::validateProto ) )
return false;
ConfigInfo newConfig;
Config::parseProto( configData, &newConfig );
if ( Config::toString( *config.storable ) ==
Config::toString( newConfig ) )
{
verbosePrintf( "No changes made to config\n" );
return false;
}
verbosePrintf( "Updating configuration...\n" );
config.storable->MergeFrom( newConfig );
verbosePrintf(
"Configuration successfully updated!\n"
"Updated configuration:\n%s", Config::toString( *config.storable ).c_str() );
return true;
}

View File

@ -67,6 +67,10 @@ public:
bool spawnEditor( std::string & data, bool( * validator ) bool spawnEditor( std::string & data, bool( * validator )
( string const &, string const & ) ); ( string const &, string const & ) );
// Edit current configuration
// returns true if configuration is changed
bool editConfigInteractively();
StorageInfo storageInfo; StorageInfo storageInfo;
EncryptionKey encryptionkey; EncryptionKey encryptionkey;
ExtendedStorageInfo extendedStorageInfo; ExtendedStorageInfo extendedStorageInfo;