1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-01-06 08:01:21 +03:00
Files
pgbackrest/lib/BackRest/RestoreFile.pm
David Steele 9be15d00f8 v0.82: Refactoring, Command-line Help, and Minor Bug Fixes
* Fixed an issue where resumed compressed backups were not preserving existing files.

* Fixed an issue where resume and incr/diff would not ensure that the prior backup had the same compression and hardlink settings.

* Fixed an issue where a cold backup using --no-start-stop could be started on a running PostgreSQL cluster without --force specified.

* Fixed an issue where a thread could be started even when none were requested.

* Fixed an issue where the pgBackRest version number was not being updated in backup.info and archive.info after an upgrade/downgrade.

* Fixed an issue where the info command was throwing an exception when the repository contained no stanzas.  Reported by Stephen Frost.

* Fixed an issue where the PostgreSQL pg_stop_backup() NOTICEs were being output to stderr.  Reported by Stephen Frost.

* Renamed recovery-setting option and section to recovery-option to be more consistent with pgBackRest naming conventions.

* Command-line help is now extracted from the same XML source that is used for the other documentation and includes much more detail.

* Code cleanup and refactoring to standardize on patterns that have evolved over time.

* Added dynamic module loading to speed up commands, especially asynchronous archiving.

* Expiration tests are now synthetic rather than based on actual backups.  This will allow development of more advanced expiration features.

* Experimental support for PostgreSQL 9.5 alpha2.  This may break when the control version or WAL magic changes in future versions but will be updated in each pgBackRest release to keep pace.  All regression tests pass except for --target-resume tests (this functionality has changed in 9.5) and there is no testing yet for .partial WAL segments.
2015-09-14 11:18:50 -04:00

156 lines
6.6 KiB
Perl

####################################################################################################################################
# RESTORE FILE MODULE
####################################################################################################################################
package BackRest::RestoreFile;
use threads;
use threads::shared;
use Thread::Queue;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);
use File::stat qw(lstat);
use lib dirname($0);
use BackRest::Common::Exception;
use BackRest::Common::Log;
use BackRest::Common::String;
use BackRest::Config::Config;
use BackRest::File;
use BackRest::Manifest;
####################################################################################################################################
# Operation constants
####################################################################################################################################
use constant OP_RESTORE_FILE => 'RestoreFile';
use constant OP_RESTORE_FILE_RESTORE_FILE => OP_RESTORE_FILE . '::restoreFile';
####################################################################################################################################
# restoreFile
#
# Restores a single file.
####################################################################################################################################
sub restoreFile
{
my $oFileHash = shift; # File to restore
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$lCopyTimeBegin, # Time that the backup begain - used for size/timestamp deltas
$bDelta, # Is restore a delta?
$bForce, # Force flag
$strBackupPath, # Backup path
$bSourceCompression, # Is the source compressed?
$strCurrentUser, # Current OS user
$strCurrentGroup, # Current OS group
$oFile, # File object
$lSizeTotal, # Total size of files to be restored
$lSizeCurrent # Current size of files restored
) =
logDebugParam
(
OP_RESTORE_FILE_RESTORE_FILE, \@_,
{name => 'lCopyTimeBegin', trace => true},
{name => 'bDelta', trace => true},
{name => 'bForce', trace => true},
{name => 'strBackupPath', trace => true},
{name => 'bSourceCompression', trace => true},
{name => 'strCurrentUser', trace => true},
{name => 'strCurrentGroup', trace => true},
{name => 'oFile', trace => true},
{name => 'lSizeTotal', trace => true},
{name => 'lSizeCurrent', trace => true}
);
# Generate destination file name
my $strDestinationFile = $oFile->pathGet(PATH_DB_ABSOLUTE, "$$oFileHash{destination_path}/$$oFileHash{file}");
# Copy flag and log message
my $bCopy = true;
my $strLog;
$lSizeCurrent += $$oFileHash{size};
if ($oFile->exists(PATH_DB_ABSOLUTE, $strDestinationFile))
{
# Perform delta if requested
if ($bDelta)
{
# If force then use size/timestamp delta
if ($bForce)
{
my $oStat = lstat($strDestinationFile);
# Make sure that timestamp/size are equal and that timestamp is before the copy start time of the backup
if (defined($oStat) && $oStat->size == $$oFileHash{size} &&
$oStat->mtime == $$oFileHash{modification_time} && $oStat->mtime < $lCopyTimeBegin)
{
$strLog = 'exists and matches size ' . $oStat->size . ' and modification time ' . $oStat->mtime;
$bCopy = false;
}
}
else
{
my ($strChecksum, $lSize) = $oFile->hashSize(PATH_DB_ABSOLUTE, $strDestinationFile);
if ($lSize == $$oFileHash{size} && ($lSize == 0 || $strChecksum eq $$oFileHash{checksum}))
{
$strLog = 'exists and ' . ($lSize == 0 ? 'is zero size' : "matches backup");
# 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($$oFileHash{modification_time}, $$oFileHash{modification_time}, $strDestinationFile)
or confess &log(ERROR, "unable to set time for ${strDestinationFile}");
$bCopy = false;
}
}
}
}
# Copy the file from the backup to the database
if ($bCopy)
{
my ($bCopyResult, $strCopyChecksum, $lCopySize) =
$oFile->copy(PATH_BACKUP_CLUSTER, (defined($$oFileHash{reference}) ? $$oFileHash{reference} : $strBackupPath) .
"/$$oFileHash{source_path}/$$oFileHash{file}" .
($bSourceCompression ? '.' . $oFile->{strCompressExtension} : ''),
PATH_DB_ABSOLUTE, $strDestinationFile,
$bSourceCompression, # Source is compressed based on backup settings
undef, undef,
$$oFileHash{modification_time},
$$oFileHash{mode},
undef,
$$oFileHash{user},
$$oFileHash{group});
if ($lCopySize != 0 && $strCopyChecksum ne $$oFileHash{checksum})
{
confess &log(ERROR, "error restoring ${strDestinationFile}: actual checksum ${strCopyChecksum} " .
"does not match expected checksum $$oFileHash{checksum}", ERROR_CHECKSUM);
}
}
&log(INFO, "restore file ${strDestinationFile}" . (defined($strLog) ? " - ${strLog}" : '') .
' (' . fileSizeFormat($$oFileHash{size}) .
($lSizeTotal > 0 ? ', ' . int($lSizeCurrent * 100 / $lSizeTotal) . '%' : '') . ')' .
($$oFileHash{size} != 0 ? " checksum $$oFileHash{checksum}" : ''));
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'lSizeCurrent', value => $lSizeCurrent, trace => true}
);
}
push @EXPORT, qw(restoreFile);
1;