1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-11-11 01:42:26 +03:00

Add Perl interface to C storage layer.

Maintaining the storage layer/drivers in two languages is burdensome.  Since the integration tests require the Perl storage layer/drivers we'll need them even after the core code is migrated to C.  Create an interface layer so the Perl code can be removed and new storage drivers/features introduced without adding Perl equivalents.

The goal is to move the integration tests to C so this interface will eventually be removed.  That being the case, the interface was designed for maximum compatibility to ease the transition.  The result looks a bit hacky but we'll improve it as needed until it can be retired.
This commit is contained in:
David Steele
2019-06-26 08:24:58 -04:00
parent bd6c0941e9
commit 4815752ccc
93 changed files with 4412 additions and 12102 deletions

View File

@@ -1,285 +0,0 @@
####################################################################################################################################
# Tests for Block Cipher
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageFilterCipherBlockPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Fcntl qw(O_RDONLY);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::LibC qw(:random :crypto);
use pgBackRest::Storage::Base;
use pgBackRest::Storage::Filter::CipherBlock;
use pgBackRest::Storage::Posix::Driver;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strFile = $self->testPath() . qw{/} . 'file.txt';
my $strFileEncrypt = $self->testPath() . qw{/} . 'file.enc.txt';
my $strFileDecrypt = $self->testPath() . qw{/} . 'file.dcr.txt';
my $strFileBin = $self->testPath() . qw{/} . 'file.bin';
my $strFileBinEncrypt = $self->testPath() . qw{/} . 'file.enc.bin';
my $strFileContent = 'TESTDATA';
my $iFileLength = length($strFileContent);
my $oDriver = new pgBackRest::Storage::Posix::Driver();
my $tCipherPass = 'areallybadkey';
my $strCipherType = 'aes-256-cbc';
my $tContent;
################################################################################################################################
if ($self->begin('new()'))
{
#---------------------------------------------------------------------------------------------------------------------------
# Create an unencrypted file
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
$self->testException(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFile), $strCipherType, $tCipherPass, {strMode => BOGUS})},
ERROR_ASSERT, 'unknown cipher mode: ' . BOGUS);
$self->testException(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFile), BOGUS, $tCipherPass)},
ERROR_ASSERT, "invalid cipher name '" . BOGUS . "'");
$self->testException(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openWrite($strFile), $strCipherType, $tCipherPass, {strMode => BOGUS})},
ERROR_ASSERT, 'unknown cipher mode: ' . BOGUS);
$self->testException(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openWrite($strFile), BOGUS, $tCipherPass)},
ERROR_ASSERT, "invalid cipher name '" . BOGUS . "'");
}
################################################################################################################################
if ($self->begin('read() and write()'))
{
my $tBuffer;
#---------------------------------------------------------------------------------------------------------------------------
# Create an plaintext file
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
# Instantiate the cipher object - default action encrypt
my $oEncryptIo = $self->testResult(sub {new pgBackRest::Storage::Filter::CipherBlock($oDriver->openRead($strFile),
$strCipherType, $tCipherPass)}, '[object]', 'new encrypt file');
$self->testResult(sub {$oEncryptIo->read(\$tBuffer, 2)}, 16, ' read 16 bytes (header)');
$self->testResult(sub {$oEncryptIo->read(\$tBuffer, 2)}, 16, ' read 16 bytes (data)');
$self->testResult(sub {$oEncryptIo->read(\$tBuffer, 2)}, 0, ' read 0 bytes');
$self->testResult(sub {$tBuffer ne $strFileContent}, true, ' data read is encrypted');
$self->testResult(sub {$oEncryptIo->close()}, true, ' close');
$self->testResult(sub {$oEncryptIo->close()}, false, ' close again');
# tBuffer is now encrypted - test write decrypts correctly
my $oDecryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock($oDriver->openWrite($strFileDecrypt),
$strCipherType, $tCipherPass, {strMode => STORAGE_DECRYPT})},
'[object]', ' new decrypt file');
$self->testResult(sub {$oDecryptFileIo->write(\$tBuffer)}, 32, ' write decrypted');
$self->testResult(sub {$oDecryptFileIo->close()}, true, ' close');
$self->testResult(sub {${$self->storageTest()->get($strFileDecrypt)}}, $strFileContent, ' data written is decrypted');
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = $strFileContent;
my $oEncryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock($oDriver->openWrite($strFileEncrypt),
$strCipherType, $tCipherPass)},
'[object]', 'new write encrypt');
$tContent = '';
$self->testResult(sub {$oEncryptFileIo->write(\$tContent)}, 0, ' attempt empty buffer write');
undef($tContent);
$self->testException(
sub {$oEncryptFileIo->write(\$tContent)}, ERROR_FILE_WRITE,
"unable to write to '${strFileEncrypt}': Use of uninitialized value");
# Encrypted length is not known so use tBuffer then test that tBuffer was encrypted
my $iWritten = $self->testResult(sub {$oEncryptFileIo->write(\$tBuffer)}, length($tBuffer), ' write encrypted');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
$tContent = $self->storageTest()->get($strFileDecrypt);
$self->testResult(sub {defined($tContent) && $tContent ne $strFileContent}, true, ' data written is encrypted');
#---------------------------------------------------------------------------------------------------------------------------
undef($tBuffer);
# Open encrypted file for decrypting
$oEncryptFileIo =
$self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFileEncrypt), $strCipherType, $tCipherPass,
{strMode => STORAGE_DECRYPT})},
'[object]', 'new read encrypted file, decrypt');
# Try to read more than the length of the data expected to be output from the decrypt and confirm the decrypted length is
# the same as the original decrypted content.
$self->testResult(sub {$oEncryptFileIo->read(\$tBuffer, $iFileLength+4)}, $iFileLength, ' read all bytes');
# Just because length is the same does not mean content is so confirm
$self->testResult($tBuffer, $strFileContent, ' data read is decrypted');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
#---------------------------------------------------------------------------------------------------------------------------
undef($tContent);
undef($tBuffer);
my $strFileBinHash = '1c7e00fd09b9dd11fc2966590b3e3274645dd031';
executeTest('cp ' . $self->dataPath() . "/filecopy.archive2.bin ${strFileBin}");
$self->testResult(
sub {cryptoHashOne('sha1', ${storageTest()->get($strFileBin)})}, $strFileBinHash, 'bin test - check sha1');
$tContent = ${storageTest()->get($strFileBin)};
$oEncryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openWrite($strFileBinEncrypt), $strCipherType, $tCipherPass)},
'[object]', ' new write encrypt');
$self->testResult(sub {$oEncryptFileIo->write(\$tContent)}, length($tContent), ' write encrypted');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
$self->testResult(
sub {cryptoHashOne('sha1', ${storageTest()->get($strFileBinEncrypt)}) ne $strFileBinHash}, true,
' check sha1 different');
my $oEncryptBinFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFileBinEncrypt), $strCipherType, $tCipherPass,
{strMode => STORAGE_DECRYPT})},
'[object]', 'new read encrypted bin file');
$self->testResult(sub {$oEncryptBinFileIo->read(\$tBuffer, 16777216)}, 16777216, ' read 16777216 bytes');
$self->testResult(sub {cryptoHashOne('sha1', $tBuffer)}, $strFileBinHash, ' check sha1 same as original');
$self->testResult(sub {$oEncryptBinFileIo->close()}, true, ' close');
# Try to read the file with the wrong passphrase
undef($tBuffer);
undef($oEncryptBinFileIo);
$oEncryptBinFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFileBinEncrypt), $strCipherType, BOGUS,
{strMode => STORAGE_DECRYPT})},
'[object]', 'new read Encrypted bin file with wrong passphrase');
$self->testResult(sub {$oEncryptBinFileIo->read(\$tBuffer, 16777216)}, 16777216, ' read all bytes');
$self->testResult(sub {cryptoHashOne('sha1', $tBuffer) ne $strFileBinHash}, true, ' check sha1 NOT same as original');
# Test file against openssl to make sure they are compatible
#---------------------------------------------------------------------------------------------------------------------------
undef($tBuffer);
$self->storageTest()->put($strFile, $strFileContent);
executeTest(
"openssl enc -k ${tCipherPass} -md sha1 -aes-256-cbc -in ${strFile} -out ${strFileEncrypt}");
$oEncryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFileEncrypt), $strCipherType, $tCipherPass,
{strMode => STORAGE_DECRYPT})},
'[object]', 'read file encrypted by openssl');
$self->testResult(sub {$oEncryptFileIo->read(\$tBuffer, 16)}, 8, ' read 8 bytes');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
$self->testResult(sub {$tBuffer}, $strFileContent, ' check content same as original');
$self->storageTest()->remove($strFile);
$self->storageTest()->remove($strFileEncrypt);
$oEncryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openWrite($strFileEncrypt), $strCipherType, $tCipherPass)},
'[object]', 'write file to be read by openssl');
$self->testResult(sub {$oEncryptFileIo->write(\$tBuffer)}, 8, ' write 8 bytes');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
executeTest(
"openssl enc -d -k ${tCipherPass} -md sha1 -aes-256-cbc -in ${strFileEncrypt} -out ${strFile}");
$self->testResult(sub {${$self->storageTest()->get($strFile)}}, $strFileContent, ' check content same as original');
# Test empty file against openssl to make sure they are compatible
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = '';
$self->storageTest()->put($strFile);
executeTest(
"openssl enc -k ${tCipherPass} -md sha1 -aes-256-cbc -in ${strFile} -out ${strFileEncrypt}");
$oEncryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFileEncrypt), $strCipherType, $tCipherPass,
{strMode => STORAGE_DECRYPT})},
'[object]', 'read empty file encrypted by openssl');
$self->testResult(sub {$oEncryptFileIo->read(\$tBuffer, 16)}, 0, ' read 0 bytes');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
$self->testResult(sub {$tBuffer}, '', ' check content same as original');
$self->storageTest()->remove($strFile);
$self->storageTest()->remove($strFileEncrypt);
$oEncryptFileIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openWrite($strFileEncrypt), $strCipherType, $tCipherPass)},
'[object]', 'write file to be read by openssl');
$self->testResult(sub {$oEncryptFileIo->write(\$tBuffer)}, 0, ' write 0 bytes');
$self->testResult(sub {$oEncryptFileIo->close()}, true, ' close');
executeTest(
"openssl enc -d -k ${tCipherPass} -md sha1 -aes-256-cbc -in ${strFileEncrypt} -out ${strFile}");
$self->testResult(sub {${$self->storageTest()->get($strFile)}}, undef, ' check content same as original');
# Error on empty file decrypt - an empty file that has been encrypted will be 32 bytes
#---------------------------------------------------------------------------------------------------------------------------
undef($tBuffer);
$self->storageTest()->put($strFileEncrypt);
$oEncryptFileIo =
$self->testResult(
sub {new pgBackRest::Storage::Filter::CipherBlock(
$oDriver->openRead($strFileEncrypt), $strCipherType, $tCipherPass,
{strMode => STORAGE_DECRYPT})},
'[object]', 'new read empty attempt decrypt');
$self->testException(sub {$oEncryptFileIo->read(\$tBuffer, 16)}, ERROR_CRYPTO, 'cipher header missing');
$self->testResult(sub {$oEncryptFileIo->close()}, true, 'close');
# OpenSSL should error on the empty file
executeTest(
"openssl enc -d -k ${tCipherPass} -md sha1 -aes-256-cbc -in ${strFileEncrypt} -out ${strFile}",
{iExpectedExitStatus => 1});
}
}
1;

View File

@@ -1,222 +0,0 @@
####################################################################################################################################
# Tests for Storage::Filter::Gzip module
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageFilterGzipPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Compress::Raw::Zlib qw(Z_OK Z_BUF_ERROR Z_DATA_ERROR);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::LibC qw(:crypto);
use pgBackRest::Storage::Base;
use pgBackRest::Storage::Filter::Gzip;
use pgBackRest::Storage::Posix::Driver;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strFile = $self->testPath() . qw{/} . 'file.txt';
my $strFileGz = "${strFile}.gz";
my $strFileContent = 'TESTDATA';
my $iFileLength = length($strFileContent);
my $oDriver = new pgBackRest::Storage::Posix::Driver();
################################################################################################################################
if ($self->begin('errorCheck()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openWrite($strFileGz))}, '[object]', 'new write');
$oGzipIo->{bWrite} = true;
$self->testException(sub {$oGzipIo->errorCheck(Z_DATA_ERROR)}, ERROR_FILE_WRITE, "unable to deflate '${strFileGz}'");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oGzipIo->errorCheck(Z_OK)}, Z_OK, 'Z_OK');
$self->testResult(sub {$oGzipIo->errorCheck(Z_BUF_ERROR)}, Z_OK, 'Z_BUF_ERROR');
#---------------------------------------------------------------------------------------------------------------------------
$oGzipIo->{bWrite} = false;
$oGzipIo->{strCompressType} = STORAGE_DECOMPRESS;
$self->testException(sub {$oGzipIo->errorCheck(Z_DATA_ERROR)}, ERROR_FILE_READ, "unable to inflate '${strFileGz}'");
}
################################################################################################################################
if ($self->begin('write()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openWrite($strFileGz), {lCompressBufferMax => 4})},
'[object]', 'new write compress');
my $tBuffer = substr($strFileContent, 0, 2);
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, 2, ' write 2 bytes');
$tBuffer = substr($strFileContent, 2, 2);
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, 2, ' write 2 bytes');
$tBuffer = substr($strFileContent, 4, 2);
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, 2, ' write 2 bytes');
$tBuffer = substr($strFileContent, 6, 2);
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, 2, ' write 2 bytes');
$tBuffer = '';
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, 0, ' write 0 bytes');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
executeTest("gzip -d ${strFileGz}");
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, ' check content');
#---------------------------------------------------------------------------------------------------------------------------
executeTest("gzip ${strFile}");
my $tFile = ${storageTest()->get($strFileGz)};
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip(
$oDriver->openWrite($strFile), {strCompressType => STORAGE_DECOMPRESS})}, '[object]', 'new write decompress');
$tBuffer = substr($tFile, 0, 10);
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, 10, ' write bytes');
$tBuffer = substr($tFile, 10);
$self->testResult(sub {$oGzipIo->write(\$tBuffer)}, length($tFile) - 10, ' write bytes');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, ' check content');
}
################################################################################################################################
if ($self->begin('read()'))
{
my $tBuffer;
#---------------------------------------------------------------------------------------------------------------------------
my $oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openWrite($strFileGz), {bWantGzip => false, iLevel => 3})},
'[object]', 'new write compress');
$self->testResult($oGzipIo->{iLevel}, 3, ' check level');
$self->testResult(sub {$oGzipIo->write(\$strFileContent, $iFileLength)}, $iFileLength, ' write');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
#---------------------------------------------------------------------------------------------------------------------------
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip(
$oDriver->openRead($strFileGz), {bWantGzip => false, strCompressType => STORAGE_DECOMPRESS})},
'[object]', 'new read decompress');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 4)}, 4, ' read 4 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 2, ' read 2 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 2, ' read 2 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 0, ' read 0 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 0, ' read 0 bytes');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
$self->testResult(sub {$oGzipIo->close()}, false, ' close again');
$self->testResult($tBuffer, $strFileContent, ' check content');
storageTest()->remove($strFileGz);
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = 'AA';
storageTest()->put($strFile, $strFileContent);
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openRead($strFile))},
'[object]', 'new read compress');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 10, ' read 10 bytes (request 2)');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 18, ' read 18 bytes (request 2)');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2)}, 0, ' read 0 bytes (request 2)');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
$self->testResult(sub {storageTest()->put($strFileGz, substr($tBuffer, 2))}, 28, ' put content');
executeTest("gzip -df ${strFileGz}");
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, ' check content');
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = undef;
executeTest('cat ' . $self->dataPath() . "/filecopy.archive2.bin | gzip -c > ${strFileGz}");
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip(
$oDriver->openRead($strFileGz), {lCompressBufferMax => 4096, strCompressType => STORAGE_DECOMPRESS})},
'[object]', 'new read decompress');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 8388608)}, 8388608, ' read 8388608 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 4194304)}, 4194304, ' read 4194304 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 4194304)}, 4194304, ' read 4194304 bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 1)}, 0, ' read 0 bytes');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
$self->testResult(cryptoHashOne('sha1', $tBuffer), '1c7e00fd09b9dd11fc2966590b3e3274645dd031', ' check content');
storageTest()->remove($strFileGz);
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = undef;
executeTest('cp ' . $self->dataPath() . "/filecopy.archive2.bin ${strFile}");
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openRead($strFile), {strCompressType => STORAGE_COMPRESS})},
'[object]', 'new read compress');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2000000) > 0}, true, ' read bytes');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2000000) > 0}, true, ' read bytes');
$self->testResult(sub {$oGzipIo->close()}, true, ' close');
$self->testResult(sub {storageTest()->put($strFileGz, $tBuffer) > 0}, true, ' put content');
executeTest("gzip -df ${strFileGz}");
$self->testResult(
sub {cryptoHashOne('sha1', ${storageTest()->get($strFile)})}, '1c7e00fd09b9dd11fc2966590b3e3274645dd031',
' check content');
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = undef;
my $oFile = $self->testResult(
sub {storageTest()->openWrite($strFile)}, '[object]', 'open file to extend during compression');
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openRead($strFile), {lCompressBufferMax => 4194304})},
'[object]', ' new read compress');
$self->testResult(sub {$oFile->write(\$strFileContent)}, length($strFileContent), ' write first block');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2000000) > 0}, true, ' read compressed first block (compression done)');
$self->testResult(sub {$oFile->write(\$strFileContent)}, length($strFileContent), ' write second block');
$self->testResult(sub {$oGzipIo->read(\$tBuffer, 2000000)}, 0, ' read compressed = 0');
$self->testResult(sub {storageTest()->put($strFileGz, $tBuffer) > 0}, true, ' put content');
executeTest("gzip -df ${strFileGz}");
$self->testResult(
sub {${storageTest()->get($strFile)}}, $strFileContent, ' check content');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strFileGz, $strFileContent);
$oGzipIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Gzip($oDriver->openRead($strFileGz), {strCompressType => STORAGE_DECOMPRESS})},
'[object]', 'new read decompress');
$self->testException(
sub {$oGzipIo->read(\$tBuffer, 1)}, ERROR_FILE_READ, "unable to inflate '${strFileGz}': incorrect header check");
}
}
1;

View File

@@ -1,107 +0,0 @@
####################################################################################################################################
# Tests for StorageFilterSha module
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageFilterShaPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::LibC qw(:crypto);
use pgBackRest::Storage::Base;
use pgBackRest::Storage::Filter::Sha;
use pgBackRest::Storage::Posix::Driver;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strFile = $self->testPath() . qw{/} . 'file.txt';
my $strFileContent = 'TESTDATA';
my $iFileLength = length($strFileContent);
my $oDriver = new pgBackRest::Storage::Posix::Driver();
################################################################################################################################
if ($self->begin('read()'))
{
my $tBuffer;
#---------------------------------------------------------------------------------------------------------------------------
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
my $oFileIo = $self->testResult(sub {$oDriver->openRead($strFile)}, '[object]', 'open read');
my $oShaIo = $self->testResult(sub {new pgBackRest::Storage::Filter::Sha($oFileIo)}, '[object]', 'new read');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 2, undef)}, 2, 'read 2 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 2, 2)}, 2, 'read 2 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 2, 4)}, 2, 'read 2 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 2, 6)}, 2, 'read 2 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 2, 8)}, 0, 'read 0 bytes');
$self->testResult(sub {$oShaIo->close()}, true, 'close');
my $strSha = $self->testResult(
sub {$oShaIo->result(STORAGE_FILTER_SHA)}, cryptoHashOne('sha1', $strFileContent),
'check hash against original content');
$self->testResult($strSha, cryptoHashOne('sha1', $tBuffer), 'check hash against buffer');
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, 'check content');
#---------------------------------------------------------------------------------------------------------------------------
$tBuffer = undef;
$oFileIo = $self->testResult(
sub {$oDriver->openRead($self->dataPath() . '/filecopy.archive2.bin')}, '[object]', 'open read');
$oShaIo = $self->testResult(sub {new pgBackRest::Storage::Filter::Sha($oFileIo)}, '[object]', 'new read');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 8388608)}, 8388608, ' read 8388608 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 4194304)}, 4194304, ' read 4194304 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 4194304)}, 4194304, ' read 4194304 bytes');
$self->testResult(sub {$oShaIo->read(\$tBuffer, 1)}, 0, ' read 0 bytes');
$self->testResult(sub {$oShaIo->close()}, true, ' close');
$self->testResult(sub {$oShaIo->close()}, false, ' close again to make sure nothing bad happens');
$self->testResult($oShaIo->result(STORAGE_FILTER_SHA), '1c7e00fd09b9dd11fc2966590b3e3274645dd031', ' check hash');
$self->testResult(cryptoHashOne('sha1', $tBuffer), '1c7e00fd09b9dd11fc2966590b3e3274645dd031', ' check content');
}
################################################################################################################################
if ($self->begin('write()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oFileIo = $self->testResult(sub {$oDriver->openWrite($strFile, {bAtomic => true})}, '[object]', 'open write');
my $oShaIo = $self->testResult(
sub {new pgBackRest::Storage::Filter::Sha($oFileIo)}, '[object]', 'new');
my $tBuffer = substr($strFileContent, 0, 2);
$self->testResult(sub {$oShaIo->write(\$tBuffer)}, 2, 'write 2 bytes');
$tBuffer = substr($strFileContent, 2, 2);
$self->testResult(sub {$oShaIo->write(\$tBuffer)}, 2, 'write 2 bytes');
$tBuffer = substr($strFileContent, 4, 2);
$self->testResult(sub {$oShaIo->write(\$tBuffer)}, 2, 'write 2 bytes');
$tBuffer = substr($strFileContent, 6, 2);
$self->testResult(sub {$oShaIo->write(\$tBuffer)}, 2, 'write 2 bytes');
$tBuffer = '';
$self->testResult(sub {$oShaIo->write(\$tBuffer)}, 0, 'write 0 bytes');
$self->testResult(sub {$oShaIo->close()}, true, 'close');
my $strSha = $self->testResult(
sub {$oShaIo->result(STORAGE_FILTER_SHA)}, cryptoHashOne('sha1', $strFileContent),
'check hash against original content');
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, 'check content');
}
}
1;

View File

@@ -61,11 +61,12 @@ sub run
#-------------------------------------------------------------------------------------------------------------------------------
if ($self->begin("storageLocal()"))
{
$self->testResult(sub {storageLocal($self->testPath())->put($strFile, $strFileContent)}, $iFileSize, 'put');
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, ' check put');
$self->testResult(sub {storageLocal($self->testPath())->put("/tmp/${strFile}", $strFileContent)}, $iFileSize, 'put');
$self->testResult(sub {${storageTest()->get("/tmp/${strFile}")}}, $strFileContent, ' check put');
$self->testResult(sub {storageLocal($self->testPath())->put($strFile, $strFileContent)}, $iFileSize, 'put cache storage');
$self->testResult(sub {${storageTest()->get($strFile)}}, $strFileContent, ' check put');
$self->testResult(
sub {storageLocal($self->testPath())->put("/tmp/${strFile}", $strFileContent)}, $iFileSize, 'put cache storage');
$self->testResult(sub {${storageTest()->get("/tmp/${strFile}")}}, $strFileContent, ' check put');
}
#-------------------------------------------------------------------------------------------------------------------------------
@@ -109,44 +110,6 @@ sub run
$self->testResult(
sub {storageRepo()->pathGet(STORAGE_REPO_BACKUP . '/file')}, $self->testPath() . '/repo/backup/db/file',
'check backup file');
#---------------------------------------------------------------------------------------------------------------------------
# Insert a bogus rule to generate an error
storageRepo()->{hRule}{'<BOGUS>'} =
{
fnRule => storageRepo()->{hRule}{&STORAGE_REPO_ARCHIVE}{fnRule},
};
$self->testException(sub {storageRepo()->pathGet('<BOGUS>')}, ERROR_ASSERT, 'invalid <REPO> storage rule <BOGUS>');
}
#-------------------------------------------------------------------------------------------------------------------------------
if ($self->begin("storageRepo() encryption"))
{
my $strStanzaEncrypt = 'test-encrypt';
$self->optionTestSet(CFGOPT_REPO_CIPHER_TYPE, CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC);
$self->testException(
sub {$self->configTestLoad(CFGCMD_ARCHIVE_PUSH)}, ERROR_OPTION_REQUIRED,
'archive-push command requires option: repo1-cipher-pass');
# Set the encryption passphrase and confirm passphrase and type have been set in the storage object
$self->optionTestSet(CFGOPT_REPO_CIPHER_PASS, 'x');
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
$self->testResult(sub {storageRepo({strStanza => $strStanzaEncrypt})->cipherType() eq
CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC}, true, 'encryption type set');
$self->testResult(sub {storageRepo({strStanza => $strStanzaEncrypt})->cipherPassUser() eq 'x'}, true,
'encryption passphrase set');
# Cannot change encryption after it has been set (cached values not reset)
$self->optionTestClear(CFGOPT_REPO_CIPHER_TYPE);
$self->optionTestClear(CFGOPT_REPO_CIPHER_PASS);
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
$self->testResult(sub {storageRepo({strStanza => $strStanzaEncrypt})->cipherType() eq
CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC}, true, 'encryption type not reset');
$self->testResult(sub {storageRepo({strStanza => $strStanzaEncrypt})->cipherPassUser() eq 'x'}, true,
'encryption passphrase not reset');
}
}

View File

@@ -1,475 +0,0 @@
####################################################################################################################################
# Tests for Storage::Local module
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageLocalPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Config::Config;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::LibC qw(:crypto);
use pgBackRest::Storage::Filter::Sha;
use pgBackRest::Storage::Base;
use pgBackRest::Storage::Local;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Env::Host::HostBackupTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# initModule - common objects and variables used by all tests.
####################################################################################################################################
sub initModule
{
my $self = shift;
# Local path
$self->{strPathLocal} = $self->testPath() . '/local';
# Create the dynamic rule
my $fnRule = sub
{
my $strRule = shift;
my $strFile = shift;
my $xData = shift;
if ($strRule eq '<fn-rule-1>')
{
return "fn-rule-1/${xData}" . (defined($strFile) ? "/${strFile}" : '');
}
else
{
return 'fn-rule-2/' . (defined($strFile) ? "${strFile}/${strFile}" : 'no-file');
}
};
# Create the rule hash
my $hRule =
{
'<static-rule>' => 'static-rule-path',
'<fn-rule-1>' =>
{
fnRule => $fnRule,
xData => 'test',
},
'<fn-rule-2>' =>
{
fnRule => $fnRule,
},
};
# Create local storage
$self->{oStorageLocal} = new pgBackRest::Storage::Local(
$self->pathLocal(), new pgBackRest::Storage::Posix::Driver(), {hRule => $hRule, bAllowTemp => false});
# Create encrypted storage
$self->{oStorageEncrypt} = new pgBackRest::Storage::Local(
$self->testPath(), new pgBackRest::Storage::Posix::Driver(),
{hRule => $hRule, bAllowTemp => false, strCipherType => CFGOPTVAL_REPO_CIPHER_TYPE_AES_256_CBC});
# Remote path
$self->{strPathRemote} = $self->testPath() . '/remote';
# Create the repo path so the remote won't complain that it's missing
mkdir($self->pathRemote())
or confess &log(ERROR, "unable to create repo directory '" . $self->pathRemote() . qw{'});
# Remove repo path now that the remote is created
rmdir($self->{strPathRemote})
or confess &log(ERROR, "unable to remove repo directory '" . $self->pathRemote() . qw{'});
# Create remote storage
$self->{oStorageRemote} = new pgBackRest::Storage::Local(
$self->pathRemote(), new pgBackRest::Storage::Posix::Driver(), {hRule => $hRule});
}
####################################################################################################################################
# initTest - initialization before each test
####################################################################################################################################
sub initTest
{
my $self = shift;
executeTest(
'ssh ' . $self->backrestUser() . '\@' . $self->host() . ' mkdir -m 700 ' . $self->pathRemote(), {bSuppressStdErr => true});
executeTest('mkdir -m 700 ' . $self->pathLocal());
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Define test file
my $strFile = 'file.txt';
my $strFileCopy = 'file.txt.copy';
my $strFileHash = 'bbbcf2c59433f68f22376cd2439d6cd309378df6';
my $strFileContent = 'TESTDATA';
my $iFileSize = length($strFileContent);
################################################################################################################################
if ($self->begin("pathGet()"))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->pathGet('<static-rule>/test', {bTemp => true})},
ERROR_ASSERT, "temp file not supported for storage '" . $self->storageLocal()->pathBase() . "'");
$self->testException(
sub {$self->storageRemote()->pathGet('<static-rule>', {bTemp => true})},
ERROR_ASSERT, 'file part must be defined when temp file specified');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageRemote()->pathGet('/file', {bTemp => true})}, "/file.tmp", 'absolute path temp');
$self->testResult(sub {$self->storageRemote()->pathGet('/file')}, "/file", 'absolute path file');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->pathGet('file')}, $self->storageLocal()->pathBase() . '/file', 'relative path');
$self->testResult(
sub {$self->storageRemote()->pathGet('file', {bTemp => true})},
$self->storageRemote()->pathBase() . '/file.tmp', 'relative path temp');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->pathGet('<static-rule/file')}, ERROR_ASSERT, "found < but not > in '<static-rule/file'");
$self->testException(
sub {$self->storageLocal()->pathGet('<bogus-rule>')}, ERROR_ASSERT, "storage rule '<bogus-rule>' does not exist");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->pathGet('<static-rule>/file')},
$self->storageLocal()->pathBase() . '/static-rule-path/file', 'static rule file');
$self->testResult(
sub {$self->storageLocal()->pathGet('<static-rule>')},
$self->storageLocal()->pathBase() . '/static-rule-path', 'static rule path');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->pathGet('<fn-rule-1>/file')},
$self->storageLocal()->pathBase() . '/fn-rule-1/test/file', 'function rule 1 file');
$self->testResult(
sub {$self->storageLocal()->pathGet('<fn-rule-2>/file')},
$self->storageLocal()->pathBase() . '/fn-rule-2/file/file', 'function rule 2 file');
$self->testResult(
sub {$self->storageLocal()->pathGet('<fn-rule-1>')},
$self->storageLocal()->pathBase() . '/fn-rule-1/test', 'function rule 1 path');
$self->testResult(
sub {$self->storageLocal()->pathGet('<fn-rule-2>')},
$self->storageLocal()->pathBase() . '/fn-rule-2/no-file', 'function rule 2 no file');
}
################################################################################################################################
if ($self->begin('openWrite()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oFileIo = $self->testResult(sub {$self->storageLocal()->openWrite($strFile)}, '[object]', 'open write');
$self->testResult(sub {$oFileIo->write(\$strFileContent)}, $iFileSize, "write $iFileSize bytes");
$self->testResult(sub {$oFileIo->close()}, true, 'close');
# Check that it is not encrypted
$self->testResult(sub {$self->storageLocal()->encrypted($strFile)}, false, 'test storage not encrypted');
}
################################################################################################################################
if ($self->begin('put()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($self->storageLocal()->openWrite($strFile))}, 0, 'put empty');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($strFile)}, 0, 'put empty (all defaults)');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($self->storageLocal()->openWrite($strFile), $strFileContent)}, $iFileSize, 'put');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($self->storageLocal()->openWrite($strFile), \$strFileContent)}, $iFileSize,
'put reference');
}
################################################################################################################################
if ($self->begin('openRead()'))
{
my $tContent;
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->openRead($strFile, {bIgnoreMissing => true})}, undef, 'ignore missing');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->openRead($strFile)}, ERROR_FILE_MISSING,
"unable to open '" . $self->storageLocal()->pathBase() . "/${strFile}': No such file or directory");
#---------------------------------------------------------------------------------------------------------------------------
executeTest('sudo touch ' . $self->pathLocal() . "/${strFile} && sudo chmod 700 " . $self->pathLocal() . "/${strFile}");
$self->testException(
sub {$self->storageLocal()->openRead($strFile)}, ERROR_FILE_OPEN,
"unable to open '" . $self->storageLocal()->pathBase() . "/${strFile}': Permission denied");
executeTest('sudo rm ' . $self->pathLocal() . "/${strFile}");
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($self->storageLocal()->openWrite($strFile), $strFileContent);
my $oFileIo = $self->testResult(sub {$self->storageLocal()->openRead($strFile)}, '[object]', 'open read');
$self->testResult(sub {$oFileIo->read(\$tContent, $iFileSize)}, $iFileSize, "read $iFileSize bytes");
$self->testResult($tContent, $strFileContent, ' check read');
#---------------------------------------------------------------------------------------------------------------------------
$oFileIo = $self->testResult(
sub {$self->storageLocal()->openRead($strFile, {rhyFilter => [{strClass => STORAGE_FILTER_SHA}]})}, '[object]',
'open read + checksum');
undef($tContent);
$self->testResult(sub {$oFileIo->read(\$tContent, $iFileSize)}, $iFileSize, "read $iFileSize bytes");
$self->testResult(sub {$oFileIo->close()}, true, 'close');
$self->testResult($tContent, $strFileContent, ' check read');
$self->testResult($oFileIo->result(STORAGE_FILTER_SHA), cryptoHashOne('sha1', $strFileContent), ' check hash');
}
################################################################################################################################
if ($self->begin('get()'))
{
my $tBuffer;
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->get($self->storageLocal()->openRead($strFile, {bIgnoreMissing => true}))}, undef,
'get missing');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($strFile);
$self->testResult(sub {${$self->storageLocal()->get($strFile)}}, undef, 'get empty');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($strFile, $strFileContent);
$self->testResult(sub {${$self->storageLocal()->get($strFile)}}, $strFileContent, 'get');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {${$self->storageLocal()->get($self->storageLocal()->openRead($strFile))}}, $strFileContent, 'get from io');
}
################################################################################################################################
if ($self->begin('hashSize()'))
{
my $tBuffer;
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($strFile, $strFileContent)}, 8, 'put');
$self->testResult(
sub {$self->storageLocal()->hashSize($strFile)},
qw{(} . cryptoHashOne('sha1', $strFileContent) . ', ' . $iFileSize . qw{)}, ' check hash/size');
$self->testResult(
sub {$self->storageLocal()->hashSize(BOGUS, {bIgnoreMissing => true})}, "([undef], [undef])",
' check missing hash/size');
}
################################################################################################################################
if ($self->begin('copy()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->copy($self->storageLocal()->openRead($strFile), $strFileCopy)}, ERROR_FILE_MISSING,
"unable to open '" . $self->storageLocal()->pathBase() . "/${strFile}': No such file or directory");
$self->testResult(
sub {$self->storageLocal()->exists($strFileCopy)}, false, ' destination does not exist');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->copy(
$self->storageLocal()->openRead($strFile, {bIgnoreMissing => true}),
$self->storageLocal()->openWrite($strFileCopy))},
false, 'missing source io');
$self->testResult(
sub {$self->storageLocal()->exists($strFileCopy)}, false, ' destination does not exist');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->copy($self->storageLocal()->openRead($strFile), $strFileCopy)}, ERROR_FILE_MISSING,
"unable to open '" . $self->storageLocal()->pathBase() . "/${strFile}': No such file or directory");
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($strFile, $strFileContent);
$self->testResult(sub {$self->storageLocal()->copy($strFile, $strFileCopy)}, true, 'copy filename->filename');
$self->testResult(sub {${$self->storageLocal()->get($strFileCopy)}}, $strFileContent, ' check copy');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->remove($strFileCopy);
$self->testResult(
sub {$self->storageLocal()->copy($self->storageLocal()->openRead($strFile), $strFileCopy)}, true, 'copy io->filename');
$self->testResult(sub {${$self->storageLocal()->get($strFileCopy)}}, $strFileContent, ' check copy');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->remove($strFileCopy);
$self->testResult(
sub {$self->storageLocal()->copy(
$self->storageLocal()->openRead($strFile), $self->storageLocal()->openWrite($strFileCopy))},
true, 'copy io->io');
$self->testResult(sub {${$self->storageLocal()->get($strFileCopy)}}, $strFileContent, ' check copy');
}
################################################################################################################################
if ($self->begin('info()'))
{
$self->testResult(sub {$self->storageLocal()->info($self->{strPathLocal})}, "[object]", 'stat dir successfully');
$self->testException(sub {$self->storageLocal()->info($strFile)}, ERROR_FILE_MISSING,
"unable to stat '". $self->{strPathLocal} . "/" . $strFile ."': No such file or directory");
}
################################################################################################################################
if ($self->begin('pathCreate()'))
{
my $strTestPath = $self->{strPathLocal} . "/" . BOGUS;
$self->testResult(sub {$self->storageLocal()->pathCreate($strTestPath)}, "[undef]",
"test creation of path " . $strTestPath);
$self->testException(sub {$self->storageLocal()->pathCreate($strTestPath)}, ERROR_PATH_EXISTS,
"unable to create path '". $strTestPath. "' because it already exists");
$self->testResult(sub {$self->storageLocal()->pathCreate($strTestPath, {bIgnoreExists => true})}, "[undef]",
"ignore path exists");
}
################################################################################################################################
if ($self->begin('encryption'))
{
my $strCipherPass = 'x';
$self->testResult(sub {cryptoHashOne('sha1', $strFileContent)}, $strFileHash, 'hash check contents to be written');
# Error when passphrase not passed
#---------------------------------------------------------------------------------------------------------------------------
my $oFileIo = $self->testException(sub {$self->storageEncrypt()->openWrite($strFile)},
ERROR_ASSERT, 'tCipherPass is required in Storage::Filter::CipherBlock->new');
# Write an encrypted file
#---------------------------------------------------------------------------------------------------------------------------
$oFileIo = $self->testResult(sub {$self->storageEncrypt()->openWrite($strFile, {strCipherPass => $strCipherPass})},
'[object]', 'open write');
my $iWritten = $oFileIo->write(\$strFileContent);
$self->testResult(sub {$oFileIo->close()}, true, ' close');
# Check that it is encrypted and valid for the repo encryption type
$self->testResult(sub {$self->storageEncrypt()->encryptionValid($self->storageEncrypt()->encrypted($strFile))}, true,
' test storage encrypted and valid');
$self->testResult(
sub {cryptoHashOne('sha1', ${storageTest()->get($strFile)}) ne $strFileHash}, true, ' check written sha1 different');
# Error when passphrase not passed
#---------------------------------------------------------------------------------------------------------------------------
$oFileIo = $self->testException(sub {$self->storageEncrypt()->openRead($strFile)},
ERROR_ASSERT, 'tCipherPass is required in Storage::Filter::CipherBlock->new');
# Read it and confirm it decrypts and is same as original content
#---------------------------------------------------------------------------------------------------------------------------
$oFileIo = $self->testResult(sub {$self->storageEncrypt()->openRead($strFile, {strCipherPass => $strCipherPass})},
'[object]', 'open read and decrypt');
my $strContent;
$oFileIo->read(\$strContent, $iWritten);
$self->testResult(sub {$oFileIo->close()}, true, ' close');
$self->testResult($strContent, $strFileContent, ' decrypt read equal orginal contents');
# Copy
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageEncrypt()->copy(
$self->storageEncrypt()->openRead($strFile, {strCipherPass => $strCipherPass}),
$self->storageEncrypt()->openWrite($strFileCopy, {strCipherPass => $strCipherPass}))},
true, 'copy - decrypt/encrypt');
$self->testResult(
sub {cryptoHashOne('sha1', ${$self->storageEncrypt()->get($strFileCopy, {strCipherPass => $strCipherPass})})},
$strFileHash, ' check decrypted copy file sha1 same as original plaintext file');
# Write an empty encrypted file
#---------------------------------------------------------------------------------------------------------------------------
my $strFileZero = 'file-0.txt';
my $strZeroContent = '';
$oFileIo = $self->testResult(
sub {$self->storageEncrypt()->openWrite($strFileZero, {strCipherPass => $strCipherPass})}, '[object]',
'open write for zero');
$self->testResult(sub {$oFileIo->write(\$strZeroContent)}, 0, ' zero written');
$self->testResult(sub {$oFileIo->close()}, true, ' close');
$self->testResult(sub {$self->storageEncrypt()->encrypted($strFile)}, true, ' test empty file encrypted');
# Write an unencrypted file to the encrypted storage and check if the file is valid for that storage
#---------------------------------------------------------------------------------------------------------------------------
my $strFileTest = $self->testPath() . qw{/} . 'test.file.txt';
# Create empty file
executeTest("touch ${strFileTest}");
$self->testResult(sub {$self->storageEncrypt()->encrypted($strFileTest)}, false, 'empty file so not encrypted');
# Add unencrypted content to the file
executeTest("echo -n '${strFileContent}' | tee ${strFileTest}");
$self->testResult(sub {$self->storageEncrypt()->encryptionValid($self->storageEncrypt()->encrypted($strFileTest))}, false,
'storage encryption and unencrypted file format do not match');
# Unencrypted file valid in unencrypted storage
$self->testResult(sub {$self->storageLocal()->encryptionValid($self->storageLocal()->encrypted($strFileTest))}, true,
'unencrypted file valid in unencrypted storage');
# Prepend encryption Magic Signature and test encrypted file in unencrypted storage not valid
executeTest('echo "' . CIPHER_MAGIC . '$(cat ' . $strFileTest . ')" > ' . $strFileTest);
$self->testResult(sub {$self->storageLocal()->encryptionValid($self->storageLocal()->encrypted($strFileTest))}, false,
'storage unencrypted and encrypted file format do not match');
# Test a file that does not exist
#---------------------------------------------------------------------------------------------------------------------------
$strFileTest = $self->testPath() . qw{/} . 'testfile';
$self->testException(sub {$self->storageEncrypt()->encrypted($strFileTest)}, ERROR_FILE_MISSING,
"unable to open '" . $strFileTest . "': No such file or directory");
$self->testResult(sub {$self->storageEncrypt()->encrypted($strFileTest, {bIgnoreMissing => true})}, true,
'encryption for ignore missing file returns encrypted for encrypted storage');
$self->testResult(sub {$self->storageLocal()->encrypted($strFileTest, {bIgnoreMissing => true})}, false,
'encryption for ignore missing file returns unencrypted for unencrypted storage');
}
}
####################################################################################################################################
# Getters
####################################################################################################################################
sub host {return '127.0.0.1'}
sub pathLocal {return shift->{strPathLocal}};
sub pathRemote {return shift->{strPathRemote}};
sub protocolLocal {return shift->{oProtocolLocal}};
sub protocolRemote {return shift->{oProtocolRemote}};
sub storageLocal {return shift->{oStorageLocal}};
sub storageEncrypt {return shift->{oStorageEncrypt}};
sub storageRemote {return shift->{oStorageRemote}};
1;

View File

@@ -0,0 +1,341 @@
####################################################################################################################################
# Tests for Storage::Local module
####################################################################################################################################
package pgBackRestTest::Module::Storage::StoragePerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Config::Config;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::LibC qw(:crypto);
use pgBackRest::Storage::Base;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Env::Host::HostBackupTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Define test file
my $strFile = $self->testPath() . '/file.txt';
my $strFileCopy = $self->testPath() . '/file.txt.copy';
my $strFileHash = 'bbbcf2c59433f68f22376cd2439d6cd309378df6';
my $strFileContent = 'TESTDATA';
my $iFileSize = length($strFileContent);
# Create local storage
$self->{oStorageLocal} = new pgBackRest::Storage::Storage('<LOCAL>');
################################################################################################################################
if ($self->begin("pathGet()"))
{
$self->testResult(sub {$self->storageLocal()->pathGet('file')}, '/file', 'relative path');
$self->testResult(sub {$self->storageLocal()->pathGet('/file2')}, '/file2', 'absolute path');
}
################################################################################################################################
if ($self->begin('put()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($self->storageLocal()->openWrite($strFile))}, 0, 'put empty');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($strFile)}, 0, 'put empty (all defaults)');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($self->storageLocal()->openWrite($strFile), $strFileContent)}, $iFileSize, 'put');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->put($self->storageLocal()->openWrite($strFile), \$strFileContent)}, $iFileSize,
'put reference');
}
################################################################################################################################
if ($self->begin('get()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->get($self->storageLocal()->openRead($strFile, {bIgnoreMissing => true}))}, undef,
'get missing');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($strFile);
$self->testResult(sub {${$self->storageLocal()->get($strFile)}}, undef, 'get empty');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($strFile, $strFileContent);
$self->testResult(sub {${$self->storageLocal()->get($strFile)}}, $strFileContent, 'get');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {${$self->storageLocal()->get($self->storageLocal()->openRead($strFile))}}, $strFileContent, 'get from io');
}
################################################################################################################################
if ($self->begin('hashSize()'))
{
$self->testResult(
sub {$self->storageLocal()->put($strFile, $strFileContent)}, 8, 'put');
$self->testResult(
sub {$self->storageLocal()->hashSize($strFile)},
qw{(} . cryptoHashOne('sha1', $strFileContent) . ', ' . $iFileSize . qw{)}, ' check hash/size');
$self->testResult(
sub {$self->storageLocal()->hashSize(BOGUS, {bIgnoreMissing => true})}, "([undef], [undef])",
' check missing hash/size');
}
################################################################################################################################
if ($self->begin('copy()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->copy($self->storageLocal()->openRead($strFile), $strFileCopy)}, ERROR_FILE_MISSING,
"unable to open missing file '${strFile}' for read");
$self->testResult(
sub {$self->storageLocal()->exists($strFileCopy)}, false, ' destination does not exist');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$self->storageLocal()->copy(
$self->storageLocal()->openRead($strFile, {bIgnoreMissing => true}),
$self->storageLocal()->openWrite($strFileCopy))},
false, 'missing source io');
$self->testResult(
sub {$self->storageLocal()->exists($strFileCopy)}, false, ' destination does not exist');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->copy($self->storageLocal()->openRead($strFile), $strFileCopy)}, ERROR_FILE_MISSING,
"unable to open missing file '${strFile}' for read");
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->put($strFile, $strFileContent);
$self->testResult(sub {$self->storageLocal()->copy($strFile, $strFileCopy)}, true, 'copy filename->filename');
$self->testResult(sub {${$self->storageLocal()->get($strFileCopy)}}, $strFileContent, ' check copy');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->remove($strFileCopy);
$self->testResult(
sub {$self->storageLocal()->copy($self->storageLocal()->openRead($strFile), $strFileCopy)}, true, 'copy io->filename');
$self->testResult(sub {${$self->storageLocal()->get($strFileCopy)}}, $strFileContent, ' check copy');
#---------------------------------------------------------------------------------------------------------------------------
$self->storageLocal()->remove($strFileCopy);
$self->testResult(
sub {$self->storageLocal()->copy(
$self->storageLocal()->openRead($strFile), $self->storageLocal()->openWrite($strFileCopy))},
true, 'copy io->io');
$self->testResult(sub {${$self->storageLocal()->get($strFileCopy)}}, $strFileContent, ' check copy');
}
################################################################################################################################
if ($self->begin('exists()'))
{
$self->storageLocal()->put($self->testPath() . "/test.file");
$self->testResult(sub {$self->storageLocal()->exists($self->testPath() . "/test.file")}, true, 'existing file');
$self->testResult(sub {$self->storageLocal()->exists($self->testPath() . "/test.missing")}, false, 'missing file');
$self->testResult(sub {$self->storageLocal()->exists($self->testPath())}, false, 'path');
}
################################################################################################################################
if ($self->begin('info()'))
{
$self->testResult(
sub {$self->storageLocal()->info($self->testPath())},
"{group => " . $self->group() . ", mode => 0770, type => d, user => " . $self->pgUser() . "}",
'stat dir successfully');
$self->testException(sub {$self->storageLocal()->info(BOGUS)}, ERROR_FILE_OPEN,
"unable to get info for missing path/file '/bogus'");
}
################################################################################################################################
if ($self->begin("manifest() and list()"))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->manifest($self->testPath() . '/missing')},
ERROR_PATH_MISSING, "unable to list file info for missing path '" . $self->testPath() . "/missing'");
#---------------------------------------------------------------------------------------------------------------------------
# Setup test data
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub1');
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub1/sub2');
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub2');
executeTest("echo 'TESTDATA' > " . $self->testPath() . '/test.txt');
utime(1111111111, 1111111111, $self->testPath() . '/test.txt');
executeTest('chmod 1640 ' . $self->testPath() . '/test.txt');
executeTest("echo 'TESTDATA_' > ". $self->testPath() . '/sub1/test-sub1.txt');
utime(1111111112, 1111111112, $self->testPath() . '/sub1/test-sub1.txt');
executeTest('chmod 0640 ' . $self->testPath() . '/sub1/test-sub1.txt');
executeTest("echo 'TESTDATA__' > " . $self->testPath() . '/sub1/sub2/test-sub2.txt');
utime(1111111113, 1111111113, $self->testPath() . '/sub1/sub2/test-sub2.txt');
executeTest('chmod 0646 ' . $self->testPath() . '/sub1/test-sub1.txt');
executeTest('ln ' . $self->testPath() . '/test.txt ' . $self->testPath() . '/sub1/test-hardlink.txt');
executeTest('ln ' . $self->testPath() . '/test.txt ' . $self->testPath() . '/sub1/sub2/test-hardlink.txt');
executeTest('ln -s .. ' . $self->testPath() . '/sub1/test');
executeTest('chmod 0700 ' . $self->testPath() . '/sub1/test');
executeTest('ln -s ../.. ' . $self->testPath() . '/sub1/sub2/test');
executeTest('chmod 0750 ' . $self->testPath() . '/sub1/sub2/test');
executeTest('chmod 0770 ' . $self->testPath());
$self->testResult(
sub {$self->storageLocal()->manifest($self->testPath())},
'{. => {group => ' . $self->group() . ', mode => 0770, type => d, user => ' . $self->pgUser() . '}, ' .
'sub1 => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'sub1/sub2 => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'sub1/sub2/test => {group => ' . $self->group() . ', link_destination => ../.., type => l, user => ' .
$self->pgUser() . '}, ' .
'sub1/sub2/test-hardlink.txt => ' .
'{group => ' . $self->group() . ', mode => 0640, modification_time => 1111111111, size => 9, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub1/sub2/test-sub2.txt => ' .
'{group => ' . $self->group() . ', mode => 0666, modification_time => 1111111113, size => 11, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub1/test => {group => ' . $self->group() . ', link_destination => .., type => l, user => ' . $self->pgUser() . '}, ' .
'sub1/test-hardlink.txt => ' .
'{group => ' . $self->group() . ', mode => 0640, modification_time => 1111111111, size => 9, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub1/test-sub1.txt => ' .
'{group => ' . $self->group() . ', mode => 0646, modification_time => 1111111112, size => 10, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub2 => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'test.txt => ' .
'{group => ' . $self->group() . ', mode => 0640, modification_time => 1111111111, size => 9, type => f, user => ' .
$self->pgUser() . '}}',
'complete manifest');
$self->testResult(sub {$self->storageLocal()->list($self->testPath())}, "(sub1, sub2, test.txt)", "list");
$self->testResult(sub {$self->storageLocal()->list($self->testPath(), {strExpression => "2\$"})}, "sub2", "list");
$self->testResult(
sub {$self->storageLocal()->list($self->testPath(), {strSortOrder => 'reverse'})}, "(test.txt, sub2, sub1)",
"list reverse");
$self->testResult(sub {$self->storageLocal()->list($self->testPath() . "/sub2")}, "[undef]", "list empty");
$self->testResult(
sub {$self->storageLocal()->list($self->testPath() . "/sub99", {bIgnoreMissing => true})}, "[undef]", "list missing");
$self->testException(
sub {$self->storageLocal()->list($self->testPath() . "/sub99")}, ERROR_PATH_MISSING,
"unable to list files for missing path '" . $self->testPath() . "/sub99'");
}
################################################################################################################################
if ($self->begin('move()'))
{
my $strFileCopy = "${strFile}.copy";
my $strFileSub = $self->testPath() . '/sub/file.txt';
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$self->storageLocal()->move($strFile, $strFileCopy)}, ERROR_FILE_MOVE,
"unable to move '${strFile}' to '${strFile}.copy': No such file or directory");
}
################################################################################################################################
if ($self->begin('owner()'))
{
my $strFile = $self->testPath() . "/test.txt";
$self->testException(
sub {$self->storageLocal()->owner($strFile, 'root')}, ERROR_FILE_MISSING,
"unable to stat '${strFile}': No such file or directory");
executeTest("touch ${strFile}");
$self->testException(
sub {$self->storageLocal()->owner($strFile, BOGUS)}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}' because user 'bogus' does not exist");
$self->testException(
sub {$self->storageLocal()->owner($strFile, undef, BOGUS)}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}' because group 'bogus' does not exist");
$self->testResult(sub {$self->storageLocal()->owner($strFile)}, undef, "no ownership changes");
$self->testResult(sub {$self->storageLocal()->owner($strFile, TEST_USER)}, undef, "same user");
$self->testResult(sub {$self->storageLocal()->owner($strFile, undef, TEST_GROUP)}, undef, "same group");
$self->testResult(
sub {$self->storageLocal()->owner($strFile, TEST_USER, TEST_GROUP)}, undef,
"same user, group");
$self->testException(
sub {$self->storageLocal()->owner($strFile, 'root', undef)}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}': Operation not permitted");
$self->testException(
sub {$self->storageLocal()->owner($strFile, undef, 'root')}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}': Operation not permitted");
executeTest("sudo chown :root ${strFile}");
$self->testResult(
sub {$self->storageLocal()->owner($strFile, undef, TEST_GROUP)}, undef, "change group back from root");
}
################################################################################################################################
if ($self->begin('pathCreate()'))
{
my $strTestPath = $self->testPath() . "/" . BOGUS;
$self->testResult(sub {$self->storageLocal()->pathCreate($strTestPath)}, "[undef]",
"test creation of path " . $strTestPath);
$self->testException(sub {$self->storageLocal()->pathCreate($strTestPath)}, ERROR_PATH_CREATE,
"unable to create path '". $strTestPath. "'");
$self->testResult(sub {$self->storageLocal()->pathCreate($strTestPath, {bIgnoreExists => true})}, "[undef]",
"ignore path exists");
}
################################################################################################################################
if ($self->begin('pathExists()'))
{
$self->storageLocal()->put($self->testPath() . "/test.file");
$self->testResult(sub {$self->storageLocal()->pathExists($self->testPath() . "/test.file")}, false, 'existing file');
$self->testResult(sub {$self->storageLocal()->pathExists($self->testPath() . "/test.missing")}, false, 'missing file');
$self->testResult(sub {$self->storageLocal()->pathExists($self->testPath())}, true, 'path');
}
################################################################################################################################
if ($self->begin('pathSync()'))
{
$self->testResult(sub {$self->storageLocal()->pathSync($self->testPath())}, "[undef]", "test path sync");
}
}
####################################################################################################################################
# Getters
####################################################################################################################################
# sub host {return '127.0.0.1'}
# sub pathLocal {return shift->{strPathLocal}};
# sub pathRemote {return shift->{strPathRemote}};
sub storageLocal {return shift->{oStorageLocal}};
# sub storageEncrypt {return shift->{oStorageEncrypt}};
# sub storageRemote {return shift->{oStorageRemote}};
1;

View File

@@ -1,444 +0,0 @@
####################################################################################################################################
# Posix Driver Tests
####################################################################################################################################
package pgBackRestTest::Module::Storage::StoragePosixPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use File::Basename qw(basename dirname);
use IO::Socket::UNIX;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Storage::Posix::Driver;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strFile = $self->testPath() . '/file.txt';
my $strFileContent = 'TESTDATA';
my $iFileLength = length($strFileContent);
my $iFileLengthHalf = int($iFileLength / 2);
# Test driver
my $oPosix = new pgBackRest::Storage::Posix::Driver();
################################################################################################################################
if ($self->begin('exists()'))
{
my $strPathSub = $self->testPath() . '/sub';
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oPosix->exists($strFile)}, false, 'file');
#---------------------------------------------------------------------------------------------------------------------------
executeTest("sudo mkdir ${strPathSub} && sudo chmod 700 ${strPathSub}");
$self->testResult(
sub {$oPosix->pathExists($strPathSub)}, true, 'path');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$oPosix->exists("${strPathSub}/file")}, ERROR_FILE_EXISTS,
"unable to test if file '${strPathSub}/file' exists: Permission denied");
}
################################################################################################################################
if ($self->begin("manifestList()"))
{
#---------------------------------------------------------------------------------------------------------------------------
my @stryFile = ('.', 'test.txt');
$self->testResult(
sub {$oPosix->manifestList($self->testPath(), \@stryFile)},
'{. => {group => ' . $self->group() . ', mode => 0770, type => d, user => ' . $self->pgUser() . '}}',
'skip missing file');
}
################################################################################################################################
if ($self->begin("manifestStat()"))
{
#---------------------------------------------------------------------------------------------------------------------------
my $strFile = $self->testPath() . '/test.txt';
$self->testResult(sub {$oPosix->manifestStat($strFile)}, '[undef]', 'ignore missing file');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strFile, "TEST");
utime(1111111111, 1111111111, $strFile);
executeTest('chmod 1640 ' . $strFile);
$self->testResult(
sub {$oPosix->manifestStat($strFile)},
'{group => ' . $self->group() .
', mode => 1640, modification_time => 1111111111, size => 4, type => f, user => ' . $self->pgUser() . '}',
'stat file');
#---------------------------------------------------------------------------------------------------------------------------
my $strSocketFile = $self->testPath() . '/test.socket';
# Create a socket to test invalid files
my $oSocket = IO::Socket::UNIX->new(Type => SOCK_STREAM(), Local => $strSocketFile, Listen => 1);
$self->testException(
sub {$oPosix->manifestStat($strSocketFile)}, ERROR_FILE_INVALID,
"${strSocketFile} is not of type directory, file, or link");
# Cleanup socket
$oSocket->close();
storageTest()->remove($strSocketFile);
#---------------------------------------------------------------------------------------------------------------------------
my $strTestPath = $self->testPath() . '/public_dir';
storageTest()->pathCreate($strTestPath, {strMode => '0750'});
$self->testResult(
sub {$oPosix->manifestStat($strTestPath)},
'{group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}',
'stat directory');
#---------------------------------------------------------------------------------------------------------------------------
my $strTestLink = $self->testPath() . '/public_dir_link';
symlink($strTestPath, $strTestLink)
or confess &log(ERROR, "unable to create symlink from ${strTestPath} to ${strTestLink}");
$self->testResult(
sub {$oPosix->manifestStat($strTestLink)},
'{group => ' . $self->group() . ", link_destination => ${strTestPath}, type => l, user => " . $self->pgUser() . '}',
'stat link');
}
################################################################################################################################
if ($self->begin("manifestRecurse()"))
{
#---------------------------------------------------------------------------------------------------------------------------
my $strTestPath = $self->testPath() . '/public_dir';
my $strTestFile = "${strTestPath}/test.txt";
$self->testException(
sub {my $hManifest = {}; $oPosix->manifestRecurse($strTestFile, undef, 0, $hManifest); $hManifest},
ERROR_FILE_MISSING, "unable to stat '${strTestFile}': No such file or directory");
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->pathCreate($strTestPath, {strMode => '0750'});
$self->testResult(
sub {my $hManifest = {}; $oPosix->manifestRecurse($strTestPath, undef, 0, $hManifest); $hManifest},
'{. => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}}',
'empty directory manifest');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strTestFile, "TEST");
utime(1111111111, 1111111111, $strTestFile);
executeTest('chmod 0750 ' . $strTestFile);
storageTest()->pathCreate("${strTestPath}/sub", {strMode => '0750'});
$self->testResult(
sub {my $hManifest = {}; $oPosix->manifestRecurse(
$self->testPath(), basename($strTestPath), 1, $hManifest); $hManifest},
'{public_dir => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'public_dir/sub => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'public_dir/' . basename($strTestFile) . ' => {group => ' . $self->group() .
', mode => 0750, modification_time => 1111111111, size => 4, type => f, user => ' . $self->pgUser() . '}}',
'directory and file manifest');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {my $hManifest = {}; $oPosix->manifestRecurse($strTestFile, undef, 0, $hManifest); $hManifest},
'{' . basename($strTestFile) . ' => {group => ' . $self->group() .
', mode => 0750, modification_time => 1111111111, size => 4, type => f, user => ' . $self->pgUser() . '}}',
'single file manifest');
}
################################################################################################################################
if ($self->begin("manifest()"))
{
#---------------------------------------------------------------------------------------------------------------------------
my $strMissingFile = $self->testPath() . '/missing';
$self->testException(
sub {$oPosix->manifest($strMissingFile)},
ERROR_FILE_MISSING, "unable to stat '${strMissingFile}': No such file or directory");
#---------------------------------------------------------------------------------------------------------------------------
# Setup test data
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub1');
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub1/sub2');
executeTest("echo 'TESTDATA' > " . $self->testPath() . '/test.txt');
utime(1111111111, 1111111111, $self->testPath() . '/test.txt');
executeTest('chmod 1640 ' . $self->testPath() . '/test.txt');
executeTest("echo 'TESTDATA_' > ". $self->testPath() . '/sub1/test-sub1.txt');
utime(1111111112, 1111111112, $self->testPath() . '/sub1/test-sub1.txt');
executeTest('chmod 0640 ' . $self->testPath() . '/sub1/test-sub1.txt');
executeTest("echo 'TESTDATA__' > " . $self->testPath() . '/sub1/sub2/test-sub2.txt');
utime(1111111113, 1111111113, $self->testPath() . '/sub1/sub2/test-sub2.txt');
executeTest('chmod 0646 ' . $self->testPath() . '/sub1/test-sub1.txt');
executeTest('ln ' . $self->testPath() . '/test.txt ' . $self->testPath() . '/sub1/test-hardlink.txt');
executeTest('ln ' . $self->testPath() . '/test.txt ' . $self->testPath() . '/sub1/sub2/test-hardlink.txt');
executeTest('ln -s .. ' . $self->testPath() . '/sub1/test');
executeTest('chmod 0700 ' . $self->testPath() . '/sub1/test');
executeTest('ln -s ../.. ' . $self->testPath() . '/sub1/sub2/test');
executeTest('chmod 0750 ' . $self->testPath() . '/sub1/sub2/test');
executeTest('chmod 0770 ' . $self->testPath());
$self->testResult(
sub {$oPosix->manifest($self->testPath())},
'{. => {group => ' . $self->group() . ', mode => 0770, type => d, user => ' . $self->pgUser() . '}, ' .
'sub1 => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'sub1/sub2 => {group => ' . $self->group() . ', mode => 0750, type => d, user => ' . $self->pgUser() . '}, ' .
'sub1/sub2/test => {group => ' . $self->group() . ', link_destination => ../.., type => l, user => ' .
$self->pgUser() . '}, ' .
'sub1/sub2/test-hardlink.txt => ' .
'{group => ' . $self->group() . ', mode => 1640, modification_time => 1111111111, size => 9, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub1/sub2/test-sub2.txt => ' .
'{group => ' . $self->group() . ', mode => 0666, modification_time => 1111111113, size => 11, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub1/test => {group => ' . $self->group() . ', link_destination => .., type => l, user => ' . $self->pgUser() . '}, ' .
'sub1/test-hardlink.txt => ' .
'{group => ' . $self->group() . ', mode => 1640, modification_time => 1111111111, size => 9, type => f, user => ' .
$self->pgUser() . '}, ' .
'sub1/test-sub1.txt => ' .
'{group => ' . $self->group() . ', mode => 0646, modification_time => 1111111112, size => 10, type => f, user => ' .
$self->pgUser() . '}, ' .
'test.txt => ' .
'{group => ' . $self->group() . ', mode => 1640, modification_time => 1111111111, size => 9, type => f, user => ' .
$self->pgUser() . '}}',
'complete manifest');
}
################################################################################################################################
if ($self->begin('openRead() & Posix::FileRead'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$oPosix->openRead($strFile)}, ERROR_FILE_MISSING, "unable to open '${strFile}': No such file or directory");
#---------------------------------------------------------------------------------------------------------------------------
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
$self->testResult(
sub {$oPosix->openRead($strFile)}, '[object]', 'open read');
}
################################################################################################################################
if ($self->begin('openWrite() & Posix::FileWrite'))
{
my $tContent = $strFileContent;
#---------------------------------------------------------------------------------------------------------------------------
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
executeTest("chmod 600 ${strFile} && sudo chown root:root ${strFile}");
$self->testException(
sub {new pgBackRest::Storage::Posix::FileRead($oPosix, $strFile)}, ERROR_FILE_OPEN,
"unable to open '${strFile}': Permission denied");
executeTest("sudo rm -rf ${strFile}");
#---------------------------------------------------------------------------------------------------------------------------
my $oPosixIo = $self->testResult(
sub {new pgBackRest::Storage::Posix::FileWrite($oPosix, $strFile)}, '[object]', 'open');
$tContent = undef;
$self->testException(
sub {$oPosixIo->write(\$tContent)}, ERROR_FILE_WRITE, "unable to write to '${strFile}': Use of uninitialized value");
$tContent = substr($strFileContent, 0, $iFileLengthHalf);
$self->testResult(
sub {$oPosixIo->write(\$tContent)}, $iFileLengthHalf, 'write part 1');
$tContent = substr($strFileContent, $iFileLengthHalf);
$self->testResult(
sub {$oPosixIo->write(\$tContent)}, $iFileLength - $iFileLengthHalf,
'write part 2');
$oPosixIo->close();
$tContent = undef;
$self->testResult(
sub {(new pgBackRest::Storage::Posix::FileRead($oPosix, $strFile))->read(\$tContent, $iFileLength)},
$iFileLength, 'check write content length');
$self->testResult($tContent, $strFileContent, 'check write content');
#---------------------------------------------------------------------------------------------------------------------------
$oPosixIo = $self->testResult(
sub {new pgBackRest::Storage::Posix::FileWrite(
$oPosix, "${strFile}.atomic", {bAtomic => true, strMode => '0666', lTimestamp => time(), bSync => false})},
'[object]', 'open');
$self->testResult(sub {$oPosixIo->write(\$tContent, $iFileLength)}, $iFileLength, 'write');
$self->testResult(sub {$oPosixIo->close()}, true, 'close');
$self->testResult(sub {${storageTest()->get("${strFile}.atomic")}}, $strFileContent, 'check content');
#---------------------------------------------------------------------------------------------------------------------------
$oPosixIo = $self->testResult(
sub {new pgBackRest::Storage::Posix::FileWrite($oPosix, $strFile)}, '[object]', 'open');
$self->testResult(sub {$oPosixIo->close()}, true, 'close');
undef($oPosixIo);
# Test that a premature destroy (from error or otherwise) does not rename the file
#---------------------------------------------------------------------------------------------------------------------------
my $strFileAbort = $self->testPath() . '/file-abort.txt';
my $strFileAbortTmp = "${strFileAbort}.tmp";
$oPosixIo = $self->testResult(
sub {new pgBackRest::Storage::Posix::FileWrite($oPosix, $strFileAbort, {bAtomic => true})}, '[object]', 'open');
$oPosixIo->write(\$strFileContent);
undef($oPosixIo);
$self->testResult(sub {$oPosix->exists($strFileAbort)}, false, 'destination file does not exist');
$self->testResult(sub {$oPosix->exists($strFileAbortTmp)}, true, 'destination file tmp exists');
#---------------------------------------------------------------------------------------------------------------------------
$oPosixIo = $self->testResult(
sub {new pgBackRest::Storage::Posix::FileWrite($oPosix, $strFile, {lTimestamp => time()})}, '[object]', 'open');
$self->testResult(sub {$oPosixIo->write(\$strFileContent, $iFileLength)}, $iFileLength, 'write');
executeTest("rm -f $strFile");
$self->testException(
sub {$oPosixIo->close()}, ERROR_FILE_WRITE, "unable to set time for '${strFile}': No such file or directory");
}
################################################################################################################################
if ($self->begin('owner()'))
{
my $strFile = $self->testPath() . "/test.txt";
$self->testException(
sub {$oPosix->owner($strFile, {strUser => 'root'})}, ERROR_FILE_MISSING,
"unable to stat '${strFile}': No such file or directory");
executeTest("touch ${strFile}");
$self->testException(
sub {$oPosix->owner($strFile, {strUser => BOGUS})}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}' because user 'bogus' does not exist");
$self->testException(
sub {$oPosix->owner($strFile, {strGroup => BOGUS})}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}' because group 'bogus' does not exist");
$self->testResult(sub {$oPosix->owner($strFile)}, undef, "no ownership changes");
$self->testResult(sub {$oPosix->owner($strFile, {strUser => TEST_USER})}, undef, "same user");
$self->testResult(sub {$oPosix->owner($strFile, {strGroup => TEST_GROUP})}, undef, "same group");
$self->testResult(
sub {$oPosix->owner($strFile, {strUser => TEST_USER, strGroup => TEST_GROUP})}, undef, "same user, group");
$self->testException(
sub {$oPosix->owner($strFile, {strUser => 'root'})}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}': Operation not permitted");
$self->testException(
sub {$oPosix->owner($strFile, {strGroup => 'root'})}, ERROR_FILE_OWNER,
"unable to set ownership for '${strFile}': Operation not permitted");
executeTest("sudo chown :root ${strFile}");
$self->testResult(
sub {$oPosix->owner($strFile, {strGroup => TEST_GROUP})}, undef, "change group back from root");
}
################################################################################################################################
if ($self->begin('pathCreate()'))
{
my $strPathParent = $self->testPath() . '/parent';
my $strPathSub = "${strPathParent}/sub1/sub2";
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oPosix->pathCreate($strPathParent)}, undef, 'parent path');
$self->testResult(
sub {$oPosix->pathExists($strPathParent)}, true, ' check path');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$oPosix->pathCreate($strPathParent)}, ERROR_PATH_EXISTS,
"unable to create path '${strPathParent}' because it already exists");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oPosix->pathCreate($strPathParent, {bIgnoreExists => true})}, undef, 'path already exists');
#---------------------------------------------------------------------------------------------------------------------------
executeTest("sudo chown root:root ${strPathParent} && sudo chmod 700 ${strPathParent}");
$self->testException(
sub {$oPosix->pathCreate($strPathSub)}, ERROR_PATH_CREATE,
"unable to create path '${strPathSub}': Permission denied");
#---------------------------------------------------------------------------------------------------------------------------
executeTest("rmdir ${strPathParent}");
$self->testException(
sub {$oPosix->pathCreate($strPathSub)}, ERROR_PATH_MISSING,
"unable to create path '${strPathSub}' because parent does not exist");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oPosix->pathCreate($strPathSub, {bCreateParent => true})}, undef, 'path with parents');
$self->testResult(
sub {$oPosix->pathExists($strPathSub)}, true, ' check path');
}
################################################################################################################################
if ($self->begin('move()'))
{
my $strFileCopy = "${strFile}.copy";
my $strFileSub = $self->testPath() . '/sub/file.txt';
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$oPosix->move($strFile, $strFileCopy)}, ERROR_FILE_MISSING,
"unable to move '${strFile}' because it is missing");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {storageTest()->put($strFile, $strFileContent)}, $iFileLength, 'put');
$self->testResult(
sub {$oPosix->move($strFile, $strFileCopy)}, undef, 'simple move');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {$oPosix->move($strFileCopy, $strFileSub)}, ERROR_PATH_MISSING,
"unable to move '${strFileCopy}' to missing path '" . dirname($strFileSub) . "'");
#---------------------------------------------------------------------------------------------------------------------------
executeTest('sudo mkdir ' . dirname($strFileSub) . ' && sudo chmod 700 ' . dirname($strFileSub));
$self->testException(
sub {$oPosix->move($strFileCopy, $strFileSub)}, ERROR_FILE_MOVE,
"unable to move '${strFileCopy}' to '${strFileSub}': Permission denied");
executeTest('sudo rmdir ' . dirname($strFileSub));
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oPosix->move($strFileCopy, $strFileSub, {bCreatePath => true})}, undef, 'create parent path');
}
}
1;

View File

@@ -1,167 +0,0 @@
####################################################################################################################################
# S3 Authentication Tests
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageS3AuthPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use POSIX qw(strftime);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Storage::S3::Auth;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
################################################################################################################################
if ($self->begin('s3DateTime'))
{
$self->testResult(sub {s3DateTime(1491267845)}, '20170404T010405Z', 'format date/time');
#---------------------------------------------------------------------------------------------------------------------------
waitRemainder();
$self->testResult(sub {s3DateTime()}, strftime("%Y%m%dT%H%M%SZ", gmtime()), 'format current date/time');
}
################################################################################################################################
if ($self->begin('s3CanonicalRequest'))
{
$self->testResult(
sub {s3CanonicalRequest(
'GET', qw(/), 'list-type=2',
{'host' => 'bucket.s3.amazonaws.com', 'x-amz-date' => '20170606T121212Z',
'x-amz-content-sha256' => '705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9'},
'705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9')},
"(GET\n/\nlist-type=2\nhost:bucket.s3.amazonaws.com\n" .
"x-amz-content-sha256:705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9\n" .
"x-amz-date:20170606T121212Z\n\nhost;x-amz-content-sha256;x-amz-date\n" .
'705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9' .
', host;x-amz-content-sha256;x-amz-date)',
'canonical request');
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(
sub {s3CanonicalRequest(
'GET', qw(/), 'list-type=2', {'Host' => 'bucket.s3.amazonaws.com'},
'705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9')},
ERROR_ASSERT, "header 'Host' must be lower case");
}
################################################################################################################################
if ($self->begin('s3SigningKey'))
{
$self->testResult(
sub {unpack('H*', s3SigningKey('20170412', 'us-east-1', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'))},
'705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9', 'signing key');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {unpack('H*', s3SigningKey('20170412', 'us-east-1', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'))},
'705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9', 'same signing key from cache');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {unpack('H*', s3SigningKey('20170505', 'us-west-1', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'))},
'c1a1cb590bbc38ba789c8e5695a1ec0cd7fd44c6949f922e149005a221524c09', 'new signing key');
}
################################################################################################################################
if ($self->begin('s3StringToSign'))
{
$self->testResult(
sub {s3StringToSign(
'20170412T141414Z', 'us-east-1', '705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9')},
"AWS4-HMAC-SHA256\n20170412T141414Z\n20170412/us-east-1/s3/aws4_request\n" .
"705636ecdedffc09f140497bcac3be1e8d069008ecc6a8029e104d6291b4e4e9",
'string to sign');
}
################################################################################################################################
if ($self->begin('s3AuthorizationHeader'))
{
$self->testResult(
sub {s3AuthorizationHeader(
'us-east-1', 'bucket.s3.amazonaws.com', 'GET', qw(/), 'list-type=2', '20170606T121212Z',
{'authorization' => BOGUS, 'host' => 'bucket.s3.amazonaws.com', 'x-amz-date' => '20170606T121212Z'},
'AKIAIOSFODNN7EXAMPLE', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', undef,
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')},
'({authorization => AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20170606/us-east-1/s3/aws4_request,' .
'SignedHeaders=host;x-amz-content-sha256;x-amz-date,' .
'Signature=cb03bf1d575c1f8904dabf0e573990375340ab293ef7ad18d049fc1338fd89b3,' .
' host => bucket.s3.amazonaws.com,' .
' x-amz-content-sha256 => e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,' .
' x-amz-date => 20170606T121212Z}, ' .
"GET\n" .
"/\n" .
"list-type=2\n" .
"host:bucket.s3.amazonaws.com\n" .
"x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" .
"x-amz-date:20170606T121212Z\n" .
"\n" .
"host;x-amz-content-sha256;x-amz-date\n" .
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, " .
"host;x-amz-content-sha256;x-amz-date, " .
"AWS4-HMAC-SHA256\n" .
"20170606T121212Z\n" .
"20170606/us-east-1/s3/aws4_request\n" .
"4f2d4ee971f579e60ba6b3895e87434e17b1260f04392f02b512c1e8bada72dd)",
'authorization header request');
$self->testResult(
sub {s3AuthorizationHeader(
'us-east-1', 'bucket.s3.amazonaws.com', 'GET', qw(/), 'list-type=2', '20170606T121212Z',
{'authorization' => BOGUS, 'host' => 'bucket.s3.amazonaws.com', 'x-amz-date' => '20170606T121212Z'},
'AKIAIOSFODNN7EXAMPLE', 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
'AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQW' .
'LWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGd' .
'QrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU' .
'9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz' .
'+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==',
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')},
'({authorization => AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20170606/us-east-1/s3/aws4_request,' .
'SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token,' .
'Signature=c12565bf5d7e0ef623f76d66e09e5431aebef803f6a25a01c586525f17e474a3,' .
' host => bucket.s3.amazonaws.com,' .
' x-amz-content-sha256 => e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,' .
' x-amz-date => 20170606T121212Z, x-amz-security-token => AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4H' .
'IZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZ' .
'ampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+sc' .
'qKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==}, ' .
"GET\n" .
"/\n" .
"list-type=2\n" .
"host:bucket.s3.amazonaws.com\n" .
"x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" .
"x-amz-date:20170606T121212Z\n" .
"x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIe" .
"oIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlR" .
"d8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJab" .
"IQwj2ICCR/oLxBA==\n" .
"\n" .
"host;x-amz-content-sha256;x-amz-date;x-amz-security-token\n" .
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, " .
"host;x-amz-content-sha256;x-amz-date;x-amz-security-token, " .
"AWS4-HMAC-SHA256\n" .
"20170606T121212Z\n" .
"20170606/us-east-1/s3/aws4_request\n" .
"c171e7a68355ef4e0e6e1003d2d4a79a7b06e7424e3000ba619f5f7882a3251e)",
'authorization header request with token');
}
}
1;

View File

@@ -1,113 +0,0 @@
####################################################################################################################################
# S3 SSL Certificate Tests
#
# Verify that SSL certificate validation works on live S3 servers.
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageS3CertPerlTest;
use parent 'pgBackRestTest::Env::ConfigEnvTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Storable qw(dclone);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::Protocol::Storage::Helper;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Common::VmTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Use long random string so bucket lookups will fail and expose access errors
my $strBucket = 'bnBfyKpXR8ZqQY5RXszxemRgvtmjXd4tf5HkFYhTpT9BndUCYMDy5NCCyRz';
my $strEndpoint = 's3-us-west-2.amazonaws.com';
my $strRegion = 'us-west-2';
# Options
$self->optionTestSet(CFGOPT_REPO_TYPE, CFGOPTVAL_REPO_TYPE_S3);
$self->optionTestSet(CFGOPT_REPO_S3_KEY, BOGUS);
$self->optionTestSet(CFGOPT_REPO_S3_KEY_SECRET, BOGUS);
$self->optionTestSet(CFGOPT_REPO_S3_TOKEN, BOGUS);
$self->optionTestSet(CFGOPT_REPO_S3_BUCKET, $strBucket);
$self->optionTestSet(CFGOPT_REPO_S3_ENDPOINT, $strEndpoint);
$self->optionTestSet(CFGOPT_REPO_S3_REGION, $strRegion);
$self->optionTestSet(CFGOPT_STANZA, $self->stanza());
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
################################################################################################################################
if ($self->begin('validation'))
{
if ($self->vm eq VM_U12)
{
&log(INFO, 'cannot test - certificates are no longer maintained for ' . $self->vm());
}
else
{
#-----------------------------------------------------------------------------------------------------------------------
if ($self->vm() eq VM_CO7)
{
# Tests fails on co7 because by default certs cannot be located. This logic may need to be changed in the future if
# this bug gets fixed by Red Hat. UPDATE: The behavior changed here but it does not seems to be fixed.
$self->testException(
sub {storageRepo({strStanza => 'test1'})->list('/')}, ERROR_HOST_CONNECT,
'SSL connect attempt failed with unknown error error.*certificate verify failed',
'cert verify fails on ' . VM_CO7);
# It should work when verification is disabled
$self->optionTestSetBool(CFGOPT_REPO_S3_VERIFY_TLS, false);
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
$self->testException(
sub {storageRepo({strStanza => 'test2'})->list('/')}, ERROR_PROTOCOL, 'S3 request error \[403\] Forbidden.*',
'connection succeeds with verification disabled, (expected) error on invalid access key');
$self->optionTestClear(CFGOPT_REPO_S3_VERIFY_TLS);
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
}
#-----------------------------------------------------------------------------------------------------------------------
# CO7 doesn't locate certs automatically so specify the path
if ($self->vm() eq VM_CO7)
{
$self->optionTestSet(CFGOPT_REPO_S3_CA_FILE, '/etc/pki/tls/certs/ca-bundle.crt');
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
}
$self->testException(
sub {storageRepo({strStanza => 'test3'})->list('/')}, ERROR_PROTOCOL, 'S3 request error \[403\] Forbidden.*',
'connection succeeds, (expected) error on invalid access key');
if ($self->vm() eq VM_CO7)
{
$self->optionTestClear(CFGOPT_REPO_S3_CA_FILE);
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
}
#-----------------------------------------------------------------------------------------------------------------------
$self->optionTestSet(CFGOPT_REPO_S3_CA_PATH, '/bogus');
$self->configTestLoad(CFGCMD_ARCHIVE_PUSH);
$self->testException(
sub {storageRepo({strStanza => 'test4'})->list('/')}, ERROR_HOST_CONNECT,
$self->vm() eq VM_CO6 ? 'SSL connect attempt failed with unknown error.*certificate verify failed' : 'No such file or directory',
'invalid ca path');
}
}
}
1;

View File

@@ -1,219 +0,0 @@
####################################################################################################################################
# S3 Storage Tests
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageS3PerlTest;
use parent 'pgBackRestTest::Env::S3EnvTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::Common::String;
use pgBackRest::LibC qw(:crypto);
use pgBackRest::Storage::S3::Driver;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# initTest
####################################################################################################################################
sub initTest
{
my $self = shift;
executeTest("$self->{strS3Command} rm --recursive s3://pgbackrest-dev");
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Initialize the driver
my $oS3 = $self->initS3();
my $oStorage = new pgBackRest::Storage::Local('', $oS3);
# Test variables
my $strFile = 'file.txt';
my $strFileContent = 'TESTDATA';
my $iFileLength = length($strFileContent);
################################################################################################################################
if ($self->begin('exists()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oStorage->exists($strFile)}, false, 'root file does not exist');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strFile, $strFileContent);
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile} s3://pgbackrest-dev");
$self->testResult(sub {$oStorage->exists($strFile)}, true, 'root file exists');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oStorage->pathExists('/path/to')}, false, 'sub path does not exist');
$self->testResult(sub {$oStorage->exists("/path/to/${strFile}")}, false, 'sub file does not exist');
#---------------------------------------------------------------------------------------------------------------------------
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile} s3://pgbackrest-dev/path/to/${strFile}");
$self->testResult(sub {$oStorage->pathExists('/path/to')}, true, 'sub path exists');
# $oStorage->pathExists('/path/to');
$self->testResult(sub {$oStorage->exists("/path/to/${strFile}")}, true, 'sub file exists');
}
################################################################################################################################
if ($self->begin('manifest()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oStorage->manifest('')}, '{. => {type => d}}', 'no files');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strFile, $strFileContent);
storageTest()->put("${strFile}2", $strFileContent . '2');
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile} s3://pgbackrest-dev");
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile}2 s3://pgbackrest-dev/path/to/${strFile}2");
$self->testResult(
sub {$oStorage->manifest('')},
'{. => {type => d}, file.txt => {size => 8, type => f}, path => {type => d}, path/to => {type => d},' .
' path/to/file.txt2 => {size => 9, type => f}}',
'root path');
$self->testResult(
sub {$oStorage->manifest('/path/to')}, '{. => {type => d}, file.txt2 => {size => 9, type => f}}', 'sub path');
}
################################################################################################################################
if ($self->begin('list()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oStorage->list('')}, '[undef]', 'no files');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strFile, $strFileContent);
storageTest()->put("${strFile}2", $strFileContent . '2');
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile} s3://pgbackrest-dev");
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile}2 s3://pgbackrest-dev/path/to/${strFile}2");
$self->testResult(sub {$oStorage->list('')}, '(file.txt, path)', 'root path');
$self->testResult(sub {$oStorage->list('/path/to')}, 'file.txt2', 'sub path');
}
################################################################################################################################
if ($self->begin('remove()'))
{
#---------------------------------------------------------------------------------------------------------------------------
$oStorage->put($strFile, $strFileContent);
$oStorage->put("/path/to/${strFile}2", $strFileContent);
$oStorage->put("/path/to/${strFile}3", $strFileContent);
$oStorage->put("/path/to/${strFile}4 \@+", $strFileContent);
$self->testResult(
sub {$oStorage->manifest('/')},
'{. => {type => d}, file.txt => {size => 8, type => f}, path => {type => d}, path/to => {type => d},' .
' path/to/file.txt2 => {size => 8, type => f}, path/to/file.txt3 => {size => 8, type => f},' .
' path/to/file.txt4 @+ => {size => 8, type => f}}',
'check manifest');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oStorage->remove('/path/to', {bRecurse => true})}, true, 'remove subpath');
$self->testResult(
sub {$oStorage->manifest('/')},
'{. => {type => d}, file.txt => {size => 8, type => f}}', 'check manifest');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oStorage->remove($strFile)}, true, 'remove file');
$self->testResult(sub {$oStorage->manifest('/')}, '{. => {type => d}}', 'check manifest');
}
################################################################################################################################
if ($self->begin('info()'))
{
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strFile, $strFileContent);
storageTest()->put("${strFile}2", $strFileContent . '2');
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile} s3://pgbackrest-dev");
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile}2 s3://pgbackrest-dev");
executeTest("$self->{strS3Command} cp " . $self->testPath() . "/${strFile}2 s3://pgbackrest-dev/path/to/${strFile}2");
$self->testResult(sub {$oStorage->info($strFile)->size()}, 8, 'file size');
$self->testResult(sub {$oStorage->info("/path/to/${strFile}2")->size()}, 9, 'file 2 size');
}
################################################################################################################################
if ($self->begin('openRead() && S3::FileRead'))
{
# Create a random 1mb file
my $strRandomFile = $self->testPath() . '/random@1mb.bin';
executeTest("dd if=/dev/urandom of=${strRandomFile} bs=1024k count=1", {bSuppressStdErr => true});
my $strRandom = ${storageTest()->get($strRandomFile)};
executeTest("$self->{strS3Command} cp ${strRandomFile} s3://pgbackrest-dev/path/to/${strFile}");
#---------------------------------------------------------------------------------------------------------------------------
my $tBuffer;
my $oFileRead = $self->testResult(sub {$oS3->openRead("/path/to/${strFile}")}, '[object]', 'open read');
$self->testResult(sub {$oFileRead->read(\$tBuffer, 524288)}, 524288, ' read half');
$self->testResult(sub {$oFileRead->read(\$tBuffer, 524288)}, 524288, ' read half');
$self->testResult(sub {$oFileRead->read(\$tBuffer, 512)}, 0, ' read 0');
$self->testResult(length($tBuffer), 1048576, ' check length');
$self->testResult(cryptoHashOne('sha1', $tBuffer), cryptoHashOne('sha1', $strRandom), ' check hash');
}
################################################################################################################################
if ($self->begin('openWrite() && S3::FileWrite'))
{
# Create a random 1mb file
my $strRandomFile = $self->testPath() . '/random1mb.bin';
executeTest("dd if=/dev/urandom of=${strRandomFile} bs=1024k count=1", {bSuppressStdErr => true});
my $strRandom = ${storageTest()->get($strRandomFile)};
#---------------------------------------------------------------------------------------------------------------------------
my $oFileWrite = $self->testResult(sub {$oS3->openWrite("/path/to/${strFile}")}, '[object]', 'open write');
$self->testResult(sub {$oFileWrite->name()}, "/path/to/${strFile}", ' check filename');
$self->testResult(sub {$oFileWrite->close()}, true, ' close without writing');
#---------------------------------------------------------------------------------------------------------------------------
$oFileWrite = $self->testResult(sub {$oS3->openWrite("/path/to/${strFile}" . '.@')}, '[object]', 'open write');
$self->testResult(sub {$oFileWrite->write()}, 0, ' write undef');
$self->testResult(sub {$oFileWrite->write(\$strFileContent)}, $iFileLength, ' write');
$oFileWrite->close();
$self->testResult(sub {$oS3->exists("/path/to/${strFile}" . '.@')}, true, 'destination file exists');
# Test that a premature destroy (from error or otherwise) does not rename the file
#---------------------------------------------------------------------------------------------------------------------------
$oFileWrite = $self->testResult(sub {$oS3->openWrite("/path/to/abort.file" . '.@')}, '[object]', 'open write');
$self->testResult(sub {$oFileWrite->write()}, 0, ' write undef');
$self->testResult(sub {$oFileWrite->write(\$strFileContent)}, $iFileLength, ' write');
undef($oFileWrite);
$self->testResult(sub {$oS3->exists("/path/to/abort.file")}, false, 'destination file does not exist');
#---------------------------------------------------------------------------------------------------------------------------
$oFileWrite = $self->testResult(sub {$oS3->openWrite("/path/to/${strFile}")}, '[object]', 'open write');
for (my $iIndex = 1; $iIndex <= 17; $iIndex++)
{
$self->testResult(sub {$oFileWrite->write(\$strRandom)}, 1024 * 1024, ' write 1mb');
}
$self->testResult(sub {$oFileWrite->close()}, true, ' close');
}
}
1;

View File

@@ -1,184 +0,0 @@
####################################################################################################################################
# S3 Request Tests
####################################################################################################################################
package pgBackRestTest::Module::Storage::StorageS3RequestPerlTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use IO::Socket::SSL;
use POSIX qw(strftime);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Http::Client;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Storage::S3::Request;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# Port to use for testing
####################################################################################################################################
use constant HTTPS_TEST_PORT => 9443;
####################################################################################################################################
# httpsServerResponse
####################################################################################################################################
sub httpsServerResponse
{
my $self = shift;
my $iResponseCode = shift;
my $strContent = shift;
# Write header
$self->{oConnection}->write("HTTP/1.1 ${iResponseCode} GenericMessage\r\n");
$self->{oConnection}->write(HTTP_HEADER_CONTENT_LENGTH . ': ' . (defined($strContent) ? length($strContent) : 0) . "\r\n");
# Write new line before content (even if there isn't any)
$self->{oConnection}->write("\r\n");
# Write content
if (defined($strContent))
{
$self->{oConnection}->write($strContent);
}
# This will block until the connection is closed by the client
$self->{oConnection}->read();
}
####################################################################################################################################
# httpsServerAccept
####################################################################################################################################
sub httpsServerAccept
{
my $self = shift;
# Wait for a connection
$self->{oConnection} = $self->{oSocketServer}->accept()
or confess "failed to accept or handshake $!, $SSL_ERROR";
&log(INFO, " * socket server connected");
}
####################################################################################################################################
# httpsServer
####################################################################################################################################
sub httpsServer
{
my $self = shift;
my $fnServer = shift;
# Fork off the server
if (fork() == 0)
{
# Run server function
$fnServer->();
exit 0;
}
}
####################################################################################################################################
# Start the https testing server
####################################################################################################################################
sub initModule
{
my $self = shift;
# Open the domain socket
$self->{oSocketServer} = IO::Socket::SSL->new(
LocalAddr => '127.0.0.1', LocalPort => HTTPS_TEST_PORT, Listen => 1, SSL_cert_file => CERT_FAKE_SERVER,
SSL_key_file => CERT_FAKE_SERVER_KEY)
or confess "unable to open https server for testing: $!";
&log(INFO, " * socket server open");
}
####################################################################################################################################
# Stop the https testing server
####################################################################################################################################
sub cleanModule
{
my $self = shift;
# Shutdown server
$self->{oSocketServer}->close();
&log(INFO, " * socket server closed");
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Initialize request object
my $oS3Request = new pgBackRest::Storage::S3::Request(
BOGUS, BOGUS, BOGUS, BOGUS, BOGUS, {strHost => '127.0.0.1', iPort => HTTPS_TEST_PORT, bVerifySsl => false});
################################################################################################################################
if ($self->begin('success'))
{
$self->httpsServer(sub
{
$self->httpsServerAccept();
$self->httpsServerResponse(200);
#-----------------------------------------------------------------------------------------------------------------------
$self->httpsServerAccept();
$self->httpsServerResponse(200);
});
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oS3Request->request(HTTP_VERB_GET)}, undef, 'successful request');
$self->testResult(sub {$oS3Request->request(HTTP_VERB_GET)}, undef, 'successful request');
}
################################################################################################################################
if ($self->begin('retry'))
{
$self->httpsServer(sub
{
$self->httpsServerAccept();
$self->httpsServerResponse(500);
$self->httpsServerAccept();
$self->httpsServerResponse(500);
$self->httpsServerAccept();
$self->httpsServerResponse(200);
#-----------------------------------------------------------------------------------------------------------------------
$self->httpsServerAccept();
$self->httpsServerResponse(500);
$self->httpsServerAccept();
$self->httpsServerResponse(500);
$self->httpsServerAccept();
$self->httpsServerResponse(500);
$self->httpsServerAccept();
$self->httpsServerResponse(500);
$self->httpsServerAccept();
$self->httpsServerResponse(500);
});
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oS3Request->request(HTTP_VERB_GET)}, undef, 'successful request after retries');
$self->testException(
sub {$oS3Request->request(HTTP_VERB_GET)}, ERROR_PROTOCOL, 'S3 request error after 5 tries \[500\] GenericMessage.*');
}
}
1;