mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-18 04:58:51 +02:00
The local command for restore is implemented entirely in C.
This is just the part of restore run by the local helper processes, not the entire command. Even so, various optimizations in the code (like pipelining and optimizations for zero-length files) should make the restore command faster on object stores.
This commit is contained in:
parent
a839830333
commit
1bc84c6474
@ -14,6 +14,12 @@
|
||||
<release-list>
|
||||
<release date="XXXX-XX-XX" version="2.15dev" title="UNDER DEVELOPMENT">
|
||||
<release-core-list>
|
||||
<release-improvement-list>
|
||||
<release-item>
|
||||
<p>The <cmd>local</cmd> command for restore is implemented entirely in C.</p>
|
||||
</release-item>
|
||||
</release-improvement-list>
|
||||
|
||||
<release-development-list>
|
||||
<release-item>
|
||||
<release-item-contributor-list>
|
||||
|
@ -172,7 +172,8 @@ sub outputRead
|
||||
# Raise the error if a warning is not requested
|
||||
if (!$bWarnOnError)
|
||||
{
|
||||
confess &log(ERROR, $strError, $hResult->{err}, $bSuppressLog);
|
||||
confess &log(
|
||||
ERROR, $strError . (defined($hResult->{errStack}) ? "\n$hResult->{errStack}" : ''), $hResult->{err}, $bSuppressLog);
|
||||
}
|
||||
|
||||
&log(WARN, $strError, $hResult->{err});
|
||||
|
@ -54,7 +54,6 @@ sub init
|
||||
my $hCommandMap =
|
||||
{
|
||||
&OP_BACKUP_FILE => sub {backupFile(@{shift()})},
|
||||
&OP_RESTORE_FILE => sub {restoreFile(@{shift()})},
|
||||
|
||||
# To be run after each command to keep the remote alive
|
||||
&OP_POST => sub {protocolKeepAlive()},
|
||||
|
@ -330,6 +330,15 @@ sub process
|
||||
eval
|
||||
{
|
||||
$hJob->{rResult} = $hLocal->{oLocal}->outputRead(true, undef, undef, true);
|
||||
|
||||
# Create a result array when the result is not already an array. The Perl locals always return an array but the C
|
||||
# locals only do so when needed.
|
||||
if (ref($hJob->{rResult}) ne 'ARRAY')
|
||||
{
|
||||
my @resultArray = (${$hJob->{rResult}});
|
||||
$hJob->{rResult} = \@resultArray;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
or do
|
||||
|
@ -25,156 +25,6 @@ use pgBackRest::Storage::Filter::Gzip;
|
||||
use pgBackRest::Storage::Filter::Sha;
|
||||
use pgBackRest::Storage::Helper;
|
||||
|
||||
####################################################################################################################################
|
||||
# restoreFile
|
||||
#
|
||||
# Restores a single file.
|
||||
####################################################################################################################################
|
||||
sub restoreFile
|
||||
{
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
$strDbFile,
|
||||
$lSize,
|
||||
$lModificationTime,
|
||||
$strChecksum,
|
||||
$bZero,
|
||||
$bForce, # Force flag
|
||||
$strRepoFile,
|
||||
$strReference,
|
||||
$strMode,
|
||||
$strUser,
|
||||
$strGroup,
|
||||
$lCopyTimeStart, # Backup start time - used for size/timestamp deltas
|
||||
$bDelta, # Is restore a delta?
|
||||
$strBackupPath, # Backup path
|
||||
$bSourceCompressed, # Is the source compressed?
|
||||
$strCipherPass, # Passphrase to decrypt the repo file (undefined if repo not encrypted)
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '::restoreFile', \@_,
|
||||
{name => 'strDbFile', trace => true},
|
||||
{name => 'lSize', trace => true},
|
||||
{name => 'lModificationTime', trace => true},
|
||||
{name => 'strChecksum', required => false, trace => true},
|
||||
{name => 'bZero', required => false, default => false, trace => true},
|
||||
{name => 'bForce', trace => true},
|
||||
{name => 'strRepoFile', trace => true},
|
||||
{name => 'strReference', required => false, trace => true},
|
||||
{name => 'strMode', trace => true},
|
||||
{name => 'strUser', trace => true},
|
||||
{name => 'strGroup', trace => true},
|
||||
{name => 'lCopyTimeStart', trace => true},
|
||||
{name => 'bDelta', trace => true},
|
||||
{name => 'strBackupPath', trace => true},
|
||||
{name => 'bSourceCompressed', trace => true},
|
||||
{name => 'strCipherPass', required => false, trace => true},
|
||||
);
|
||||
|
||||
# Does the file need to be copied?
|
||||
my $oStorageDb = storageDb();
|
||||
my $bCopy = true;
|
||||
|
||||
# Zero file if requested
|
||||
if ($bZero)
|
||||
{
|
||||
$bCopy = false;
|
||||
|
||||
my $oDestinationFileIo = $oStorageDb->openWrite(
|
||||
$strDbFile, {strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lModificationTime});
|
||||
$oDestinationFileIo->open();
|
||||
|
||||
# Now truncate to the original size. This will create a sparse file which is very efficient for this use case.
|
||||
truncate($oDestinationFileIo->handle(), $lSize);
|
||||
|
||||
$oDestinationFileIo->close();
|
||||
}
|
||||
# Perform delta if requested
|
||||
elsif ($bDelta)
|
||||
{
|
||||
my $oStat = $oStorageDb->info($strDbFile, {bIgnoreMissing => true});
|
||||
|
||||
# Do the delta if the file exists and is not a link or the link destination exists
|
||||
if (defined($oStat) &&
|
||||
(!S_ISLNK($oStat->mode) ||
|
||||
$oStorageDb->exists(
|
||||
$oStorageDb->pathAbsolute(dirname($strDbFile), $oStorageDb->{oDriver}->linkDestination($strDbFile)))))
|
||||
{
|
||||
# If force then use size/timestamp delta
|
||||
if ($bForce)
|
||||
{
|
||||
# Make sure that timestamp/size are equal and that timestamp is before the copy start time of the backup
|
||||
if (defined($oStat) && $oStat->size == $lSize &&
|
||||
$oStat->mtime == $lModificationTime && $oStat->mtime < $lCopyTimeStart)
|
||||
{
|
||||
$bCopy = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
my ($strActualChecksum, $lActualSize) = $oStorageDb->hashSize($strDbFile);
|
||||
|
||||
if ($lActualSize == $lSize && ($lSize == 0 || $strActualChecksum eq $strChecksum))
|
||||
{
|
||||
# Even if hash is the same set the time back to backup time. This helps with unit testing, but also
|
||||
# presents a pristine version of the database after restore.
|
||||
utime($lModificationTime, $lModificationTime, $strDbFile)
|
||||
or confess &log(ERROR, "unable to set time for ${strDbFile}");
|
||||
|
||||
$bCopy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Copy file from repository to database
|
||||
if ($bCopy)
|
||||
{
|
||||
# Add sha filter
|
||||
my $rhyFilter = [{strClass => STORAGE_FILTER_SHA}];
|
||||
|
||||
# Add compression
|
||||
if ($bSourceCompressed)
|
||||
{
|
||||
unshift(@{$rhyFilter}, {strClass => STORAGE_FILTER_GZIP, rxyParam => [{strCompressType => STORAGE_DECOMPRESS}]});
|
||||
}
|
||||
|
||||
# Open destination file
|
||||
my $oDestinationFileIo = $oStorageDb->openWrite(
|
||||
$strDbFile,
|
||||
{strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lModificationTime,
|
||||
rhyFilter => $rhyFilter});
|
||||
|
||||
# Copy file
|
||||
storageRepo()->copy(
|
||||
storageRepo()->openRead(
|
||||
STORAGE_REPO_BACKUP . qw(/) . (defined($strReference) ? $strReference : $strBackupPath) .
|
||||
"/${strRepoFile}" . ($bSourceCompressed ? qw{.} . COMPRESS_EXT : ''),
|
||||
{bProtocolCompress => !$bSourceCompressed && $lSize != 0, strCipherPass => $strCipherPass}),
|
||||
$oDestinationFileIo);
|
||||
|
||||
# Validate checksum
|
||||
if ($oDestinationFileIo->result(COMMON_IO_HANDLE) != 0 && $oDestinationFileIo->result(STORAGE_FILTER_SHA) ne $strChecksum)
|
||||
{
|
||||
confess &log(ERROR,
|
||||
"error restoring ${strDbFile}: actual checksum '" . $oDestinationFileIo->digest() .
|
||||
"' does not match expected checksum ${strChecksum}", ERROR_CHECKSUM);
|
||||
}
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'bCopy', value => $bCopy, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
push @EXPORT, qw(restoreFile);
|
||||
|
||||
####################################################################################################################################
|
||||
# restoreLog
|
||||
#
|
||||
|
@ -55,6 +55,8 @@ SRCS = \
|
||||
command/command.c \
|
||||
command/control/control.c \
|
||||
command/local/local.c \
|
||||
command/restore/file.c \
|
||||
command/restore/protocol.c \
|
||||
command/remote/remote.c \
|
||||
common/compress/gzip/common.c \
|
||||
common/compress/gzip/compress.c \
|
||||
@ -223,12 +225,18 @@ command/help/help.o: command/help/help.c build.auto.h common/assert.h common/deb
|
||||
command/info/info.o: command/info/info.c build.auto.h command/archive/common.h command/info/info.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h perl/exec.h postgres/interface.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CFLAGS) $(CMAKE) -c command/info/info.c -o command/info/info.o
|
||||
|
||||
command/local/local.o: command/local/local.c build.auto.h command/archive/get/protocol.h command/archive/push/protocol.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleRead.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/protocol.h protocol/client.h protocol/command.h protocol/helper.h protocol/server.h
|
||||
command/local/local.o: command/local/local.c build.auto.h command/archive/get/protocol.h command/archive/push/protocol.h command/restore/protocol.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleRead.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/protocol.h protocol/client.h protocol/command.h protocol/helper.h protocol/server.h
|
||||
$(CC) $(CFLAGS) $(CMAKE) -c command/local/local.c -o command/local/local.o
|
||||
|
||||
command/remote/remote.o: command/remote/remote.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleRead.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h config/protocol.h protocol/client.h protocol/command.h protocol/helper.h protocol/server.h storage/remote/protocol.h
|
||||
$(CC) $(CFLAGS) $(CMAKE) -c command/remote/remote.c -o command/remote/remote.o
|
||||
|
||||
command/restore/file.o: command/restore/file.c build.auto.h command/restore/file.h common/assert.h common/compress/gzip/common.h common/compress/gzip/decompress.h common/crypto/cipherBlock.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/filter/size.h common/io/io.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h storage/helper.h storage/info.h storage/posix/common.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CFLAGS) $(CMAKE) -c command/restore/file.c -o command/restore/file.o
|
||||
|
||||
command/restore/protocol.o: command/restore/protocol.c build.auto.h command/restore/file.h command/restore/protocol.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/io.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h protocol/server.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
|
||||
$(CC) $(CFLAGS) $(CMAKE) -c command/restore/protocol.c -o command/restore/protocol.o
|
||||
|
||||
common/compress/gzip/common.o: common/compress/gzip/common.c build.auto.h common/assert.h common/compress/gzip/common.h common/debug.h common/error.auto.h common/error.h common/logLevel.h common/memContext.h common/stackTrace.h common/type/convert.h
|
||||
$(CC) $(CFLAGS) $(CMAKE) -c common/compress/gzip/common.c -o common/compress/gzip/common.o
|
||||
|
||||
|
@ -5,6 +5,7 @@ Local Command
|
||||
|
||||
#include "command/archive/get/protocol.h"
|
||||
#include "command/archive/push/protocol.h"
|
||||
#include "command/restore/protocol.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io/handleRead.h"
|
||||
#include "common/io/handleWrite.h"
|
||||
@ -33,6 +34,7 @@ cmdLocal(int handleRead, int handleWrite)
|
||||
ProtocolServer *server = protocolServerNew(name, PROTOCOL_SERVICE_LOCAL_STR, read, write);
|
||||
protocolServerHandlerAdd(server, archiveGetProtocol);
|
||||
protocolServerHandlerAdd(server, archivePushProtocol);
|
||||
protocolServerHandlerAdd(server, restoreProtocol);
|
||||
protocolServerProcess(server);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
198
src/command/restore/file.c
Normal file
198
src/command/restore/file.c
Normal file
@ -0,0 +1,198 @@
|
||||
/***********************************************************************************************************************************
|
||||
Restore File
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include "command/restore/file.h"
|
||||
#include "common/compress/gzip/common.h"
|
||||
#include "common/compress/gzip/decompress.h"
|
||||
#include "common/crypto/cipherBlock.h"
|
||||
#include "common/crypto/hash.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io/filter/group.h"
|
||||
#include "common/io/filter/size.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/log.h"
|
||||
#include "config/config.h"
|
||||
#include "storage/posix/common.h"
|
||||
#include "storage/helper.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Copy a file from the backup to the specified destination
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
restoreFile(
|
||||
const String *repoFile, const String *repoFileReference, bool repoFileCompressed, const String *pgFile,
|
||||
const String *pgFileChecksum, bool pgFileZero, uint64_t pgFileSize, time_t pgFileModified, mode_t pgFileMode,
|
||||
const String *pgFileUser, const String *pgFileGroup, time_t copyTimeBegin, bool delta, bool deltaForce,
|
||||
const String *cipherPass)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STRING, repoFile);
|
||||
FUNCTION_LOG_PARAM(STRING, repoFileReference);
|
||||
FUNCTION_LOG_PARAM(BOOL, repoFileCompressed);
|
||||
FUNCTION_LOG_PARAM(STRING, pgFile);
|
||||
FUNCTION_LOG_PARAM(STRING, pgFileChecksum);
|
||||
FUNCTION_LOG_PARAM(BOOL, pgFileZero);
|
||||
FUNCTION_LOG_PARAM(UINT64, pgFileSize);
|
||||
FUNCTION_LOG_PARAM(INT64, pgFileModified);
|
||||
FUNCTION_LOG_PARAM(MODE, pgFileMode);
|
||||
FUNCTION_LOG_PARAM(STRING, pgFileUser);
|
||||
FUNCTION_LOG_PARAM(STRING, pgFileGroup);
|
||||
FUNCTION_LOG_PARAM(INT64, copyTimeBegin);
|
||||
FUNCTION_LOG_PARAM(BOOL, delta);
|
||||
FUNCTION_LOG_PARAM(BOOL, deltaForce);
|
||||
FUNCTION_TEST_PARAM(STRING, cipherPass);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(repoFile != NULL);
|
||||
ASSERT(repoFileReference != NULL);
|
||||
ASSERT(pgFile != NULL);
|
||||
|
||||
// Was the file copied?
|
||||
bool result = true;
|
||||
|
||||
// Create destination file. We may not use this but it makes sense to only create it in one place if we do.
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Perform delta if requested. Delta zero-length files to avoid overwriting the file if the timestamp is correct.
|
||||
if (delta && !pgFileZero)
|
||||
{
|
||||
// Perform delta if the file exists
|
||||
StorageInfo info = storageInfoP(storagePg(), pgFile, .ignoreMissing = true, .followLink = true);
|
||||
|
||||
if (info.exists)
|
||||
{
|
||||
// If force then use size/timestamp delta
|
||||
if (deltaForce)
|
||||
{
|
||||
// Make sure that timestamp/size are equal and that timestamp is before the copy start time of the backup
|
||||
if (info.size == pgFileSize && info.timeModified == pgFileModified && info.timeModified < copyTimeBegin)
|
||||
result = false;
|
||||
}
|
||||
// Else use size and checksum
|
||||
else
|
||||
{
|
||||
// Only continue delta if the file size is as expected
|
||||
if (info.size == pgFileSize)
|
||||
{
|
||||
// Generate checksum for the file if size is not zero
|
||||
IoFilterGroup *filterGroup = ioFilterGroupNew();
|
||||
|
||||
if (info.size != 0)
|
||||
{
|
||||
IoRead *read = storageReadIo(storageNewReadNP(storagePgWrite(), pgFile));
|
||||
ioFilterGroupAdd(filterGroup, cryptoHashNew(HASH_TYPE_SHA1_STR));
|
||||
ioReadFilterGroupSet(read, filterGroup);
|
||||
|
||||
Buffer *buffer = bufNew(ioBufferSize());
|
||||
ioReadOpen(read);
|
||||
|
||||
do
|
||||
{
|
||||
ioRead(read, buffer);
|
||||
bufUsedZero(buffer);
|
||||
}
|
||||
while (!ioReadEof(read));
|
||||
|
||||
ioReadClose(read);
|
||||
}
|
||||
|
||||
// If size and checksum are equal then no need to copy the file
|
||||
if (pgFileSize == 0 ||
|
||||
strEq(pgFileChecksum, varStr(ioFilterGroupResult(filterGroup, CRYPTO_HASH_FILTER_TYPE_STR))))
|
||||
{
|
||||
// Even if hash/size are the same set the time back to backup time. This helps with unit testing, but
|
||||
// also presents a pristine version of the database after restore.
|
||||
if (info.timeModified != pgFileModified)
|
||||
{
|
||||
THROW_ON_SYS_ERROR_FMT(
|
||||
utime(
|
||||
strPtr(storagePath(storagePg(), pgFile)),
|
||||
&((struct utimbuf){.actime = pgFileModified, .modtime = pgFileModified})) == -1,
|
||||
FileInfoError, "unable to set time for '%s'", strPtr(storagePath(storagePg(), pgFile)));
|
||||
}
|
||||
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy file from repository to database or create zero-length/sparse file
|
||||
if (result)
|
||||
{
|
||||
// Create destination file
|
||||
StorageWrite *pgFileWrite = storageNewWriteP(
|
||||
storagePgWrite(), pgFile, .modeFile = pgFileMode, .user = pgFileUser, .group = pgFileGroup,
|
||||
.timeModified = pgFileModified, .noAtomic = true, .noCreatePath = true, .noSyncPath = true);
|
||||
|
||||
// If size is zero/sparse no need to actually copy
|
||||
if (pgFileSize == 0 || pgFileZero)
|
||||
{
|
||||
ioWriteOpen(storageWriteIo(pgFileWrite));
|
||||
|
||||
// Truncate the file to specified length (note in this case the file with grow, not shrink)
|
||||
if (pgFileZero)
|
||||
{
|
||||
THROW_ON_SYS_ERROR_FMT(
|
||||
ftruncate(ioWriteHandle(storageWriteIo(pgFileWrite)), (off_t)pgFileSize) == -1, FileWriteError,
|
||||
"unable to truncate '%s'", strPtr(pgFile));
|
||||
|
||||
// Report the file as not copied
|
||||
result = false;
|
||||
}
|
||||
|
||||
ioWriteClose(storageWriteIo(pgFileWrite));
|
||||
}
|
||||
// Else perform the copy
|
||||
else
|
||||
{
|
||||
IoFilterGroup *filterGroup = ioFilterGroupNew();
|
||||
|
||||
// Add decryption filter
|
||||
if (cipherPass != NULL)
|
||||
ioFilterGroupAdd(filterGroup, cipherBlockNew(cipherModeDecrypt, cipherTypeAes256Cbc, BUFSTR(cipherPass), NULL));
|
||||
|
||||
// Add decompression filter
|
||||
if (repoFileCompressed)
|
||||
ioFilterGroupAdd(filterGroup, gzipDecompressNew(false));
|
||||
|
||||
// Add sha1 filter
|
||||
ioFilterGroupAdd(filterGroup, cryptoHashNew(HASH_TYPE_SHA1_STR));
|
||||
|
||||
// Add size filter
|
||||
ioFilterGroupAdd(filterGroup, ioSizeNew());
|
||||
|
||||
ioWriteFilterGroupSet(storageWriteIo(pgFileWrite), filterGroup);
|
||||
|
||||
// Copy file
|
||||
storageCopyNP(
|
||||
storageNewReadNP(
|
||||
storageRepo(),
|
||||
strNewFmt(
|
||||
STORAGE_REPO_BACKUP "/%s/%s%s", strPtr(repoFileReference), strPtr(repoFile),
|
||||
repoFileCompressed ? "." GZIP_EXT : "")),
|
||||
pgFileWrite);
|
||||
|
||||
// Validate checksum
|
||||
if (!strEq(pgFileChecksum, varStr(ioFilterGroupResult(filterGroup, CRYPTO_HASH_FILTER_TYPE_STR))))
|
||||
{
|
||||
THROW_FMT(
|
||||
ChecksumError,
|
||||
"error restoring '%s': actual checksum '%s' does not match expected checksum '%s'", strPtr(pgFile),
|
||||
strPtr(varStr(ioFilterGroupResult(filterGroup, CRYPTO_HASH_FILTER_TYPE_STR))), strPtr(pgFileChecksum));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(BOOL, result);
|
||||
}
|
20
src/command/restore/file.h
Normal file
20
src/command/restore/file.h
Normal file
@ -0,0 +1,20 @@
|
||||
/***********************************************************************************************************************************
|
||||
Restore File
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMAND_RESTORE_FILE_H
|
||||
#define COMMAND_RESTORE_FILE_H
|
||||
|
||||
#include "common/crypto/common.h"
|
||||
#include "common/type/string.h"
|
||||
#include "storage/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
bool restoreFile(
|
||||
const String *repoFile, const String *repoFileReference, bool repoFileCompressed, const String *pgFile,
|
||||
const String *pgFileChecksum, bool pgFileZero, uint64_t pgFileSize, time_t pgFileModified, mode_t pgFileMode,
|
||||
const String *pgFileUser, const String *pgFileGroup, time_t copyTimeBegin, bool delta, bool deltaForce,
|
||||
const String *cipherPass);
|
||||
|
||||
#endif
|
63
src/command/restore/protocol.c
Normal file
63
src/command/restore/protocol.c
Normal file
@ -0,0 +1,63 @@
|
||||
/***********************************************************************************************************************************
|
||||
Restore Protocol Handler
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "command/restore/file.h"
|
||||
#include "command/restore/protocol.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/log.h"
|
||||
#include "common/memContext.h"
|
||||
#include "config/config.h"
|
||||
#include "storage/helper.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
STRING_EXTERN(PROTOCOL_COMMAND_RESTORE_FILE_STR, PROTOCOL_COMMAND_RESTORE_FILE);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Process protocol requests
|
||||
***********************************************************************************************************************************/
|
||||
bool
|
||||
restoreProtocol(const String *command, const VariantList *paramList, ProtocolServer *server)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STRING, command);
|
||||
FUNCTION_LOG_PARAM(VARIANT_LIST, paramList);
|
||||
FUNCTION_LOG_PARAM(PROTOCOL_SERVER, server);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(command != NULL);
|
||||
|
||||
// Get the repo storage in case it is remote and encryption settings need to be pulled down
|
||||
storageRepo();
|
||||
|
||||
// Attempt to satisfy the request -- we may get requests that are meant for other handlers
|
||||
bool found = true;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
if (strEq(command, PROTOCOL_COMMAND_RESTORE_FILE_STR))
|
||||
{
|
||||
protocolServerResponse(
|
||||
server,
|
||||
VARBOOL(
|
||||
restoreFile(varStr(varLstGet(paramList, 6)),
|
||||
varLstGet(paramList, 7) ? varStr(varLstGet(paramList, 7)) : varStr(varLstGet(paramList, 13)),
|
||||
varBoolForce(varLstGet(paramList, 14)), varStr(varLstGet(paramList, 0)), varStr(varLstGet(paramList, 3)),
|
||||
varBoolForce(varLstGet(paramList, 4)), varUInt64(varLstGet(paramList, 1)),
|
||||
(time_t)varInt64Force(varLstGet(paramList, 2)), cvtZToUIntBase(strPtr(varStr(varLstGet(paramList, 8))), 8),
|
||||
varStr(varLstGet(paramList, 9)), varStr(varLstGet(paramList, 10)),
|
||||
(time_t)varInt64Force(varLstGet(paramList, 11)), varBoolForce(varLstGet(paramList, 12)),
|
||||
varBoolForce(varLstGet(paramList, 5)),
|
||||
varLstSize(paramList) == 16 ? varStr(varLstGet(paramList, 15)) : NULL)));
|
||||
}
|
||||
else
|
||||
found = false;
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(BOOL, found);
|
||||
}
|
22
src/command/restore/protocol.h
Normal file
22
src/command/restore/protocol.h
Normal file
@ -0,0 +1,22 @@
|
||||
/***********************************************************************************************************************************
|
||||
Restore Protocol Handler
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef COMMAND_RESTORE_PROTOCOL_H
|
||||
#define COMMAND_RESTORE_PROTOCOL_H
|
||||
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
#include "protocol/server.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
#define PROTOCOL_COMMAND_RESTORE_FILE "restoreFile"
|
||||
STRING_DECLARE(PROTOCOL_COMMAND_RESTORE_FILE_STR);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
bool restoreProtocol(const String *command, const VariantList *paramList, ProtocolServer *server);
|
||||
|
||||
#endif
|
@ -155,7 +155,8 @@ main(int argListSize, const char *argList[])
|
||||
case cfgCmdLocal:
|
||||
{
|
||||
if (strEq(cfgOptionStr(cfgOptCommand), CFGCMD_ARCHIVE_GET_ASYNC_STR) ||
|
||||
strEq(cfgOptionStr(cfgOptCommand), CFGCMD_ARCHIVE_PUSH_ASYNC_STR))
|
||||
strEq(cfgOptionStr(cfgOptCommand), CFGCMD_ARCHIVE_PUSH_ASYNC_STR) ||
|
||||
strEq(cfgOptionStr(cfgOptCommand), CFGCMD_RESTORE_STR))
|
||||
{
|
||||
cmdLocal(STDIN_FILENO, STDOUT_FILENO);
|
||||
}
|
||||
|
@ -11380,7 +11380,8 @@ static const EmbeddedModule embeddedModule[] =
|
||||
"\n\n"
|
||||
"if (!$bWarnOnError)\n"
|
||||
"{\n"
|
||||
"confess &log(ERROR, $strError, $hResult->{err}, $bSuppressLog);\n"
|
||||
"confess &log(\n"
|
||||
"ERROR, $strError . (defined($hResult->{errStack}) ? \"\\n$hResult->{errStack}\" : ''), $hResult->{err}, $bSuppressLog);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"&log(WARN, $strError, $hResult->{err});\n"
|
||||
@ -12384,7 +12385,6 @@ static const EmbeddedModule embeddedModule[] =
|
||||
"my $hCommandMap =\n"
|
||||
"{\n"
|
||||
"&OP_BACKUP_FILE => sub {backupFile(@{shift()})},\n"
|
||||
"&OP_RESTORE_FILE => sub {restoreFile(@{shift()})},\n"
|
||||
"\n\n"
|
||||
"&OP_POST => sub {protocolKeepAlive()},\n"
|
||||
"};\n"
|
||||
@ -12673,6 +12673,13 @@ static const EmbeddedModule embeddedModule[] =
|
||||
"eval\n"
|
||||
"{\n"
|
||||
"$hJob->{rResult} = $hLocal->{oLocal}->outputRead(true, undef, undef, true);\n"
|
||||
"\n\n\n"
|
||||
"if (ref($hJob->{rResult}) ne 'ARRAY')\n"
|
||||
"{\n"
|
||||
"my @resultArray = (${$hJob->{rResult}});\n"
|
||||
"$hJob->{rResult} = \\@resultArray;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"return true;\n"
|
||||
"}\n"
|
||||
"or do\n"
|
||||
@ -15066,140 +15073,6 @@ static const EmbeddedModule embeddedModule[] =
|
||||
"use pgBackRest::Storage::Filter::Sha;\n"
|
||||
"use pgBackRest::Storage::Helper;\n"
|
||||
"\n\n\n\n\n\n"
|
||||
"sub restoreFile\n"
|
||||
"{\n"
|
||||
"\n"
|
||||
"my\n"
|
||||
"(\n"
|
||||
"$strOperation,\n"
|
||||
"$strDbFile,\n"
|
||||
"$lSize,\n"
|
||||
"$lModificationTime,\n"
|
||||
"$strChecksum,\n"
|
||||
"$bZero,\n"
|
||||
"$bForce,\n"
|
||||
"$strRepoFile,\n"
|
||||
"$strReference,\n"
|
||||
"$strMode,\n"
|
||||
"$strUser,\n"
|
||||
"$strGroup,\n"
|
||||
"$lCopyTimeStart,\n"
|
||||
"$bDelta,\n"
|
||||
"$strBackupPath,\n"
|
||||
"$bSourceCompressed,\n"
|
||||
"$strCipherPass,\n"
|
||||
") =\n"
|
||||
"logDebugParam\n"
|
||||
"(\n"
|
||||
"__PACKAGE__ . '::restoreFile', \\@_,\n"
|
||||
"{name => 'strDbFile', trace => true},\n"
|
||||
"{name => 'lSize', trace => true},\n"
|
||||
"{name => 'lModificationTime', trace => true},\n"
|
||||
"{name => 'strChecksum', required => false, trace => true},\n"
|
||||
"{name => 'bZero', required => false, default => false, trace => true},\n"
|
||||
"{name => 'bForce', trace => true},\n"
|
||||
"{name => 'strRepoFile', trace => true},\n"
|
||||
"{name => 'strReference', required => false, trace => true},\n"
|
||||
"{name => 'strMode', trace => true},\n"
|
||||
"{name => 'strUser', trace => true},\n"
|
||||
"{name => 'strGroup', trace => true},\n"
|
||||
"{name => 'lCopyTimeStart', trace => true},\n"
|
||||
"{name => 'bDelta', trace => true},\n"
|
||||
"{name => 'strBackupPath', trace => true},\n"
|
||||
"{name => 'bSourceCompressed', trace => true},\n"
|
||||
"{name => 'strCipherPass', required => false, trace => true},\n"
|
||||
");\n"
|
||||
"\n\n"
|
||||
"my $oStorageDb = storageDb();\n"
|
||||
"my $bCopy = true;\n"
|
||||
"\n\n"
|
||||
"if ($bZero)\n"
|
||||
"{\n"
|
||||
"$bCopy = false;\n"
|
||||
"\n"
|
||||
"my $oDestinationFileIo = $oStorageDb->openWrite(\n"
|
||||
"$strDbFile, {strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lModificationTime});\n"
|
||||
"$oDestinationFileIo->open();\n"
|
||||
"\n\n"
|
||||
"truncate($oDestinationFileIo->handle(), $lSize);\n"
|
||||
"\n"
|
||||
"$oDestinationFileIo->close();\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"elsif ($bDelta)\n"
|
||||
"{\n"
|
||||
"my $oStat = $oStorageDb->info($strDbFile, {bIgnoreMissing => true});\n"
|
||||
"\n\n"
|
||||
"if (defined($oStat) &&\n"
|
||||
"(!S_ISLNK($oStat->mode) ||\n"
|
||||
"$oStorageDb->exists(\n"
|
||||
"$oStorageDb->pathAbsolute(dirname($strDbFile), $oStorageDb->{oDriver}->linkDestination($strDbFile)))))\n"
|
||||
"{\n"
|
||||
"\n"
|
||||
"if ($bForce)\n"
|
||||
"{\n"
|
||||
"\n"
|
||||
"if (defined($oStat) && $oStat->size == $lSize &&\n"
|
||||
"$oStat->mtime == $lModificationTime && $oStat->mtime < $lCopyTimeStart)\n"
|
||||
"{\n"
|
||||
"$bCopy = false;\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
"else\n"
|
||||
"{\n"
|
||||
"my ($strActualChecksum, $lActualSize) = $oStorageDb->hashSize($strDbFile);\n"
|
||||
"\n"
|
||||
"if ($lActualSize == $lSize && ($lSize == 0 || $strActualChecksum eq $strChecksum))\n"
|
||||
"{\n"
|
||||
"\n\n"
|
||||
"utime($lModificationTime, $lModificationTime, $strDbFile)\n"
|
||||
"or confess &log(ERROR, \"unable to set time for ${strDbFile}\");\n"
|
||||
"\n"
|
||||
"$bCopy = false;\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
"\n\n"
|
||||
"if ($bCopy)\n"
|
||||
"{\n"
|
||||
"\n"
|
||||
"my $rhyFilter = [{strClass => STORAGE_FILTER_SHA}];\n"
|
||||
"\n\n"
|
||||
"if ($bSourceCompressed)\n"
|
||||
"{\n"
|
||||
"unshift(@{$rhyFilter}, {strClass => STORAGE_FILTER_GZIP, rxyParam => [{strCompressType => STORAGE_DECOMPRESS}]});\n"
|
||||
"}\n"
|
||||
"\n\n"
|
||||
"my $oDestinationFileIo = $oStorageDb->openWrite(\n"
|
||||
"$strDbFile,\n"
|
||||
"{strMode => $strMode, strUser => $strUser, strGroup => $strGroup, lTimestamp => $lModificationTime,\n"
|
||||
"rhyFilter => $rhyFilter});\n"
|
||||
"\n\n"
|
||||
"storageRepo()->copy(\n"
|
||||
"storageRepo()->openRead(\n"
|
||||
"STORAGE_REPO_BACKUP . qw(/) . (defined($strReference) ? $strReference : $strBackupPath) .\n"
|
||||
"\"/${strRepoFile}\" . ($bSourceCompressed ? qw{.} . COMPRESS_EXT : ''),\n"
|
||||
"{bProtocolCompress => !$bSourceCompressed && $lSize != 0, strCipherPass => $strCipherPass}),\n"
|
||||
"$oDestinationFileIo);\n"
|
||||
"\n\n"
|
||||
"if ($oDestinationFileIo->result(COMMON_IO_HANDLE) != 0 && $oDestinationFileIo->result(STORAGE_FILTER_SHA) ne $strChecksum)\n"
|
||||
"{\n"
|
||||
"confess &log(ERROR,\n"
|
||||
"\"error restoring ${strDbFile}: actual checksum '\" . $oDestinationFileIo->digest() .\n"
|
||||
"\"' does not match expected checksum ${strChecksum}\", ERROR_CHECKSUM);\n"
|
||||
"}\n"
|
||||
"}\n"
|
||||
"\n\n"
|
||||
"return logDebugReturn\n"
|
||||
"(\n"
|
||||
"$strOperation,\n"
|
||||
"{name => 'bCopy', value => $bCopy, trace => true}\n"
|
||||
");\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"push @EXPORT, qw(restoreFile);\n"
|
||||
"\n\n\n\n\n\n"
|
||||
"sub restoreLog\n"
|
||||
"{\n"
|
||||
"\n"
|
||||
|
@ -718,6 +718,14 @@ unit:
|
||||
coverage:
|
||||
command/remote/remote: full
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: restore
|
||||
total: 1
|
||||
|
||||
coverage:
|
||||
command/restore/file: full
|
||||
command/restore/protocol: full
|
||||
|
||||
# ********************************************************************************************************************************
|
||||
- name: backup
|
||||
|
||||
|
247
test/src/module/command/restoreTest.c
Normal file
247
test/src/module/command/restoreTest.c
Normal file
@ -0,0 +1,247 @@
|
||||
/***********************************************************************************************************************************
|
||||
Test Restore Command
|
||||
***********************************************************************************************************************************/
|
||||
#include "common/compress/gzip/compress.h"
|
||||
#include "common/crypto/cipherBlock.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/io/bufferRead.h"
|
||||
#include "common/io/bufferWrite.h"
|
||||
#include "storage/posix/storage.h"
|
||||
#include "storage/helper.h"
|
||||
|
||||
#include "common/harnessConfig.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Test Run
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
testRun(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
// Start a protocol server to test the protocol directly
|
||||
Buffer *serverWrite = bufNew(8192);
|
||||
IoWrite *serverWriteIo = ioBufferWriteNew(serverWrite);
|
||||
ioWriteOpen(serverWriteIo);
|
||||
|
||||
ProtocolServer *server = protocolServerNew(
|
||||
strNew("test"), strNew("test"), ioBufferReadNew(bufNew(0)), serverWriteIo);
|
||||
|
||||
bufUsedSet(serverWrite, 0);
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("restoreFile()"))
|
||||
{
|
||||
const String *repoFileReferenceFull = strNew("20190509F");
|
||||
const String *repoFile1 = strNew("pg_data/testfile");
|
||||
|
||||
// Load Parameters
|
||||
StringList *argList = strLstNew();
|
||||
strLstAddZ(argList, "pgbackrest");
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
|
||||
strLstAdd(argList, strNewFmt("--pg1-path=%s/pg", testPath()));
|
||||
strLstAddZ(argList, "restore");
|
||||
harnessCfgLoad(strLstSize(argList), strLstPtr(argList));
|
||||
|
||||
// Create the pg path
|
||||
storagePathCreateP(storagePgWrite(), NULL, .mode = 0700);
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("sparse-zero"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
true, 0x10000000000UL, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, true, false, NULL),
|
||||
false, "zero sparse 1TB file");
|
||||
TEST_RESULT_UINT(storageInfoNP(storagePg(), strNew("sparse-zero")).size, 0x10000000000UL, " check size");
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("normal-zero"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 0, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, false, false, NULL),
|
||||
true, "zero-length file");
|
||||
TEST_RESULT_UINT(storageInfoNP(storagePg(), strNew("normal-zero")).size, 0, " check size");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
// Create a compressed encrypted repo file
|
||||
StorageWrite *ceRepoFile = storageNewWriteNP(
|
||||
storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s/%s.gz", strPtr(repoFileReferenceFull), strPtr(repoFile1)));
|
||||
IoFilterGroup *filterGroup = ioFilterGroupNew();
|
||||
ioFilterGroupAdd(filterGroup, gzipCompressNew(3, false));
|
||||
ioFilterGroupAdd(filterGroup, cipherBlockNew(cipherModeEncrypt, cipherTypeAes256Cbc, BUFSTRDEF("badpass"), NULL));
|
||||
ioWriteFilterGroupSet(storageWriteIo(ceRepoFile), filterGroup);
|
||||
|
||||
storagePutNP(ceRepoFile, BUFSTRDEF("acefile"));
|
||||
|
||||
TEST_ERROR(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, true, strNew("normal"), strNew("ffffffffffffffffffffffffffffffffffffffff"),
|
||||
false, 7, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, false, false, strNew("badpass")),
|
||||
ChecksumError,
|
||||
"error restoring 'normal': actual checksum 'd1cd8a7d11daa26814b93eb604e1d49ab4b43770' does not match expected checksum"
|
||||
" 'ffffffffffffffffffffffffffffffffffffffff'");
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, true, strNew("normal"), strNew("d1cd8a7d11daa26814b93eb604e1d49ab4b43770"),
|
||||
false, 7, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, false, false, strNew("badpass")),
|
||||
true, "copy file");
|
||||
|
||||
StorageInfo info = storageInfoNP(storagePg(), strNew("normal"));
|
||||
TEST_RESULT_BOOL(info.exists, true, " check exists");
|
||||
TEST_RESULT_UINT(info.size, 7, " check size");
|
||||
TEST_RESULT_UINT(info.mode, 0600, " check mode");
|
||||
TEST_RESULT_UINT(info.timeModified, 1557432154, " check time");
|
||||
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
|
||||
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storagePg(), strNew("normal"))))), "acefile", " check contents");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
// Create a repo file
|
||||
storagePutNP(
|
||||
storageNewWriteNP(
|
||||
storageRepoWrite(), strNewFmt(STORAGE_REPO_BACKUP "/%s/%s", strPtr(repoFileReferenceFull), strPtr(repoFile1))),
|
||||
BUFSTRDEF("atestfile"));
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, true, false, NULL),
|
||||
true, "sha1 delta missing");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storagePg(), strNew("delta"))))), "atestfile", " check contents");
|
||||
|
||||
size_t oldBufferSize = ioBufferSize();
|
||||
ioBufferSizeSet(4);
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, true, false, NULL),
|
||||
false, "sha1 delta existing");
|
||||
|
||||
ioBufferSizeSet(oldBufferSize);
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 1557432155, true, true, NULL),
|
||||
false, "sha1 delta force existing");
|
||||
|
||||
// Change the existing file so it no longer matches by size
|
||||
storagePutNP(storageNewWriteNP(storagePgWrite(), strNew("delta")), BUFSTRDEF("atestfile2"));
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, true, false, NULL),
|
||||
true, "sha1 delta existing, size differs");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storagePg(), strNew("delta"))))), "atestfile", " check contents");
|
||||
|
||||
storagePutNP(storageNewWriteNP(storagePgWrite(), strNew("delta")), BUFSTRDEF("atestfile2"));
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 1557432155, true, true, NULL),
|
||||
true, "delta force existing, size differs");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storagePg(), strNew("delta"))))), "atestfile", " check contents");
|
||||
|
||||
// Change the existing file so it no longer matches by content
|
||||
storagePutNP(storageNewWriteNP(storagePgWrite(), strNew("delta")), BUFSTRDEF("btestfile"));
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, true, false, NULL),
|
||||
true, "sha1 delta existing, content differs");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storagePg(), strNew("delta"))))), "atestfile", " check contents");
|
||||
|
||||
storagePutNP(storageNewWriteNP(storagePgWrite(), strNew("delta")), BUFSTRDEF("btestfile"));
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 1557432155, true, true, NULL),
|
||||
true, "delta force existing, timestamp differs");
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 9, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 1557432153, true, true, NULL),
|
||||
true, "delta force existing, timestamp after copy time");
|
||||
|
||||
// Change the existing file to zero-length
|
||||
storagePutNP(storageNewWriteNP(storagePgWrite(), strNew("delta")), BUFSTRDEF(""));
|
||||
|
||||
TEST_RESULT_BOOL(
|
||||
restoreFile(
|
||||
repoFile1, repoFileReferenceFull, false, strNew("delta"), strNew("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"),
|
||||
false, 0, 1557432154, 0600, strNew(testUser()), strNew(testGroup()), 0, true, false, NULL),
|
||||
false, "sha1 delta existing, content differs");
|
||||
|
||||
// Check protocol function directly
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
VariantList *paramList = varLstNew();
|
||||
varLstAdd(paramList, varNewStrZ("protocol"));
|
||||
varLstAdd(paramList, varNewUInt64(9));
|
||||
varLstAdd(paramList, varNewUInt64(1557432100));
|
||||
varLstAdd(paramList, varNewStrZ("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"));
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
varLstAdd(paramList, varNewStr(repoFile1));
|
||||
varLstAdd(paramList, NULL);
|
||||
varLstAdd(paramList, varNewStrZ("0677"));
|
||||
varLstAdd(paramList, varNewStrZ(testUser()));
|
||||
varLstAdd(paramList, varNewStrZ(testGroup()));
|
||||
varLstAdd(paramList, varNewUInt64(1557432200));
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
varLstAdd(paramList, varNewStr(repoFileReferenceFull));
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
|
||||
TEST_RESULT_BOOL(restoreProtocol(PROTOCOL_COMMAND_RESTORE_FILE_STR, paramList, server), true, "protocol restore file");
|
||||
TEST_RESULT_STR(strPtr(strNewBuf(serverWrite)), "{\"out\":true}\n", " check result");
|
||||
bufUsedSet(serverWrite, 0);
|
||||
|
||||
info = storageInfoNP(storagePg(), strNew("protocol"));
|
||||
TEST_RESULT_BOOL(info.exists, true, " check exists");
|
||||
TEST_RESULT_UINT(info.size, 9, " check size");
|
||||
TEST_RESULT_UINT(info.mode, 0677, " check mode");
|
||||
TEST_RESULT_UINT(info.timeModified, 1557432100, " check time");
|
||||
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
|
||||
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
|
||||
TEST_RESULT_STR(
|
||||
strPtr(strNewBuf(storageGetNP(storageNewReadNP(storagePg(), strNew("protocol"))))), "atestfile", " check contents");
|
||||
|
||||
paramList = varLstNew();
|
||||
varLstAdd(paramList, varNewStrZ("protocol"));
|
||||
varLstAdd(paramList, varNewUInt64(9));
|
||||
varLstAdd(paramList, varNewUInt64(1557432100));
|
||||
varLstAdd(paramList, varNewStrZ("9bc8ab2dda60ef4beed07d1e19ce0676d5edde67"));
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
varLstAdd(paramList, varNewStr(repoFile1));
|
||||
varLstAdd(paramList, varNewStr(repoFileReferenceFull));
|
||||
varLstAdd(paramList, varNewStrZ("0677"));
|
||||
varLstAdd(paramList, varNewStrZ(testUser()));
|
||||
varLstAdd(paramList, varNewStrZ(testGroup()));
|
||||
varLstAdd(paramList, varNewUInt64(1557432200));
|
||||
varLstAdd(paramList, varNewBool(true));
|
||||
varLstAdd(paramList, NULL);
|
||||
varLstAdd(paramList, varNewBool(false));
|
||||
varLstAdd(paramList, NULL);
|
||||
|
||||
TEST_RESULT_BOOL(restoreProtocol(PROTOCOL_COMMAND_RESTORE_FILE_STR, paramList, server), true, "protocol restore file");
|
||||
TEST_RESULT_STR(strPtr(strNewBuf(serverWrite)), "{\"out\":false}\n", " check result");
|
||||
bufUsedSet(serverWrite, 0);
|
||||
|
||||
// Check invalid protocol function
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_RESULT_BOOL(restoreProtocol(strNew(BOGUS_STR), paramList, server), false, "invalid function");
|
||||
}
|
||||
|
||||
FUNCTION_HARNESS_RESULT_VOID();
|
||||
}
|
Loading…
Reference in New Issue
Block a user