1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00
pgbackrest/lib/BackRest/Manifest.pm
David Steele f83f0fa54d v0.70: Stability improvements for archiving, improved logging and help
* Fixed an issue where archive-copy would fail on an incr/diff backup when hardlink=n.  In this case the pg_xlog path does not already exist and must be created. Reported by Michael Renner

* Allow duplicate WAL segments to be archived when the checksum matches.  This is necessary for some recovery scenarios.

* Allow comments/disabling in pg_backrest.conf using #.  Suggested by Michael Renner.

* Better logging before pg_start_backup() to make it clear when the backup is waiting on a checkpoint.  Suggested by Michael Renner.

* Various command behavior, help and logging fixes.  Reported by Michael Renner.

* Fixed an issue in async archiving where archive-push was not properly returning 0 when archive-max-mb was reached and moved the async check after transfer to avoid having to remove the stop file twice.  Also added unit tests for this case and improved error messages to make it clearer to the user what went wrong.  Reported by Michael Renner.

* Fixed a locking issue that could allow multiple operations of the same type against a single stanza.  This appeared to be benign in terms of data integrity but caused spurious errors while archiving and could lead to errors in backup/restore. Reported by Michael Renner.

* Replaced JSON module with JSON::PP which ships with core Perl.
2015-06-01 09:24:38 -04:00

749 lines
27 KiB
Perl

####################################################################################################################################
# MANIFEST MODULE
####################################################################################################################################
package BackRest::Manifest;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname basename);
use Time::Local qw(timelocal);
use Digest::SHA;
use lib dirname($0);
use BackRest::Exception qw(ERROR_CHECKSUM ERROR_FORMAT);
use BackRest::Utility;
use BackRest::File;
# Exports
use Exporter qw(import);
our @EXPORT = qw(MANIFEST_PATH MANIFEST_FILE MANIFEST_LINK
MANIFEST_SECTION_BACKUP MANIFEST_SECTION_BACKUP_OPTION MANIFEST_SECTION_BACKUP_PATH
MANIFEST_SECTION_BACKUP_TABLESPACE
MANIFEST_KEY_ARCHIVE_START MANIFEST_KEY_ARCHIVE_STOP MANIFEST_KEY_BASE MANIFEST_KEY_CHECKSUM MANIFEST_KEY_COMPRESS
MANIFEST_KEY_HARDLINK MANIFEST_KEY_LABEL MANIFEST_KEY_PRIOR MANIFEST_KEY_REFERENCE MANIFEST_KEY_TIMESTAMP_COPY_START
MANIFEST_KEY_TIMESTAMP_START MANIFEST_KEY_TIMESTAMP_STOP MANIFEST_KEY_TYPE MANIFEST_KEY_VERSION
MANIFEST_SUBKEY_CHECKSUM MANIFEST_SUBKEY_DESTINATION MANIFEST_SUBKEY_FUTURE MANIFEST_SUBKEY_GROUP
MANIFEST_SUBKEY_LINK MANIFEST_SUBKEY_MODE MANIFEST_SUBKEY_MODIFICATION_TIME MANIFEST_SUBKEY_PATH
MANIFEST_SUBKEY_REFERENCE MANIFEST_SUBKEY_SIZE MANIFEST_SUBKEY_USER);
####################################################################################################################################
# File/path constants
####################################################################################################################################
use constant FILE_MANIFEST => 'backup.manifest';
push @EXPORT, qw(FILE_MANIFEST);
####################################################################################################################################
# MANIFEST Constants
####################################################################################################################################
use constant
{
MANIFEST_PATH => 'path',
MANIFEST_FILE => 'file',
MANIFEST_LINK => 'link',
MANIFEST_SECTION_BACKUP => 'backup',
MANIFEST_SECTION_BACKUP_OPTION => 'backup:option',
MANIFEST_SECTION_BACKUP_PATH => 'backup:path',
MANIFEST_SECTION_BACKUP_TABLESPACE => 'backup:tablespace',
MANIFEST_KEY_ARCHIVE_START => 'archive-start',
MANIFEST_KEY_ARCHIVE_STOP => 'archive-stop',
MANIFEST_KEY_BASE => 'base',
MANIFEST_KEY_CHECKSUM => 'checksum',
MANIFEST_KEY_COMPRESS => 'compress',
MANIFEST_KEY_FORMAT => 'format',
MANIFEST_KEY_HARDLINK => 'hardlink',
MANIFEST_KEY_LABEL => 'label',
MANIFEST_KEY_PRIOR => 'prior',
MANIFEST_KEY_REFERENCE => 'reference',
MANIFEST_KEY_TIMESTAMP_COPY_START => 'timestamp-copy-start',
MANIFEST_KEY_TIMESTAMP_START => 'timestamp-start',
MANIFEST_KEY_TIMESTAMP_STOP => 'timestamp-stop',
MANIFEST_KEY_TYPE => 'type',
MANIFEST_KEY_VERSION => 'version',
MANIFEST_SUBKEY_CHECKSUM => 'checksum',
MANIFEST_SUBKEY_DESTINATION => 'link_destination',
MANIFEST_SUBKEY_FUTURE => 'future',
MANIFEST_SUBKEY_GROUP => 'group',
MANIFEST_SUBKEY_LINK => 'link',
MANIFEST_SUBKEY_MODE => 'mode',
MANIFEST_SUBKEY_MODIFICATION_TIME => 'modification_time',
MANIFEST_SUBKEY_PATH => 'path',
MANIFEST_SUBKEY_REFERENCE => 'reference',
MANIFEST_SUBKEY_SIZE => 'size',
MANIFEST_SUBKEY_USER => 'user'
};
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
my $strFileName = shift; # Manifest filename
my $bLoad = shift; # Load the manifest?
# Create the class hash
my $self = {};
bless $self, $class;
# Filename must be specified
if (!defined($strFileName))
{
confess &log(ASSERT, 'filename must be provided');
}
# Set variables
my $oManifest = {};
$self->{oManifest} = $oManifest;
$self->{strFileName} = $strFileName;
# Load the manifest if specified
if (!(defined($bLoad) && $bLoad == false))
{
ini_load($strFileName, $oManifest);
# Make sure the manifest is valid by testing checksum
my $strChecksum = $self->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_CHECKSUM);
my $strTestChecksum = $self->hash();
if ($strChecksum ne $strTestChecksum)
{
confess &log(ERROR, "backup.manifest checksum is invalid, should be ${strTestChecksum}", ERROR_CHECKSUM);
}
# Make sure that the format is current, otherwise error
my $iFormat = $self->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_FORMAT, undef, false, 0);
if ($iFormat != FORMAT)
{
confess &log(ERROR, "backup format of ${strFileName} is ${iFormat} but " . FORMAT . ' is required by this version of ' .
'PgBackRest. If you are attempting an incr/diff backup you will need to take a new full backup. ' .
"If you are trying to restore, you''ll need to use a version that supports format ${iFormat}." ,
ERROR_FORMAT);
}
}
else
{
$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_FORMAT, undef, FORMAT);
}
return $self;
}
####################################################################################################################################
# SAVE
#
# Save the manifest.
####################################################################################################################################
sub save
{
my $self = shift;
# Create the checksum
$self->hash();
# Save the config file
ini_save($self->{strFileName}, $self->{oManifest});
}
####################################################################################################################################
# HASH
#
# Generate hash for the manifest.
####################################################################################################################################
sub hash
{
my $self = shift;
my $oManifest = $self->{oManifest};
# Remove the old checksum
$self->remove(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_CHECKSUM);
my $oSHA = Digest::SHA->new('sha1');
# Calculate the checksum from section values
foreach my $strSection ($self->keys())
{
$oSHA->add($strSection);
# Calculate the checksum from key values
foreach my $strKey ($self->keys($strSection))
{
$oSHA->add($strKey);
my $strValue = $self->get($strSection, $strKey);
if (!defined($strValue))
{
confess &log(ASSERT, "section ${strSection}, key ${$strKey} has undef value");
}
# Calculate the checksum from subkey values
if (ref($strValue) eq "HASH")
{
foreach my $strSubKey ($self->keys($strSection, $strKey))
{
my $strSubValue = $self->get($strSection, $strKey, $strSubKey);
if (!defined($strSubValue))
{
confess &log(ASSERT, "section ${strSection}, key ${strKey}, subkey ${strSubKey} has undef value");
}
$oSHA->add($strSubValue);
}
}
else
{
$oSHA->add($strValue);
}
}
}
# Set the new checksum
my $strHash = $oSHA->hexdigest();
$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_CHECKSUM, undef, $strHash);
return $strHash;
}
####################################################################################################################################
# GET
#
# Get a value.
####################################################################################################################################
sub get
{
my $self = shift;
my $strSection = shift;
my $strValue = shift;
my $strSubValue = shift;
my $bRequired = shift;
my $oDefault = shift;
my $oManifest = $self->{oManifest};
# Section must always be defined
if (!defined($strSection))
{
confess &log(ASSERT, 'section is not defined');
}
# Set default for required
$bRequired = defined($bRequired) ? $bRequired : true;
# Store the result
my $oResult = undef;
if (defined($strSubValue))
{
if (!defined($strValue))
{
confess &log(ASSERT, 'subvalue requested bu value is not defined');
}
if (defined(${$oManifest}{$strSection}{$strValue}))
{
$oResult = ${$oManifest}{$strSection}{$strValue}{$strSubValue};
}
}
elsif (defined($strValue))
{
if (defined(${$oManifest}{$strSection}))
{
$oResult = ${$oManifest}{$strSection}{$strValue};
}
}
else
{
$oResult = ${$oManifest}{$strSection};
}
if (!defined($oResult) && $bRequired)
{
confess &log(ASSERT, "manifest section '$strSection'" . (defined($strValue) ? ", value '$strValue'" : '') .
(defined($strSubValue) ? ", subvalue '$strSubValue'" : '') . ' is required but not defined');
}
if (!defined($oResult) && defined($oDefault))
{
$oResult = $oDefault;
}
return $oResult
}
####################################################################################################################################
# SET
#
# Set a value.
####################################################################################################################################
sub set
{
my $self = shift;
my $strSection = shift;
my $strKey = shift;
my $strSubKey = shift;
my $strValue = shift;
my $oManifest = $self->{oManifest};
# Make sure the keys are valid
$self->valid($strSection, $strKey, $strSubKey);
if (defined($strSubKey))
{
${$oManifest}{$strSection}{$strKey}{$strSubKey} = $strValue;
}
else
{
${$oManifest}{$strSection}{$strKey} = $strValue;
}
}
####################################################################################################################################
# REMOVE
#
# Remove a value.
####################################################################################################################################
sub remove
{
my $self = shift;
my $strSection = shift;
my $strKey = shift;
my $strSubKey = shift;
my $strValue = shift;
my $oManifest = $self->{oManifest};
# Make sure the keys are valid
$self->valid($strSection, $strKey, $strSubKey, undef, true);
if (defined($strSubKey))
{
delete(${$oManifest}{$strSection}{$strKey}{$strSubKey});
}
else
{
delete(${$oManifest}{$strSection}{$strKey});
}
}
####################################################################################################################################
# VALID
#
# Determine if section, key, subkey combination is valid.
####################################################################################################################################
sub valid
{
my $self = shift;
my $strSection = shift;
my $strKey = shift;
my $strSubKey = shift;
my $strValue = shift;
my $bDelete = shift;
# Section and key must always be defined
if (!defined($strSection) || !defined($strKey))
{
confess &log(ASSERT, 'section or key is not defined');
}
# Default bDelete
$bDelete = defined($bDelete) ? $bDelete : false;
if ($strSection =~ /^.*\:(file|path|link)$/ && $strSection !~ /^backup\:path$/)
{
if (!defined($strSubKey) && $bDelete)
{
return true;
}
my $strPath = (split(':', $strSection))[0];
my $strType = (split(':', $strSection))[1];
if ($strPath eq 'tablespace')
{
$strPath = (split(':', $strSection))[1];
$strType = (split(':', $strSection))[2];
}
if (($strType eq 'path' || $strType eq 'file' || $strType eq 'link') &&
($strSubKey eq MANIFEST_SUBKEY_USER ||
$strSubKey eq MANIFEST_SUBKEY_GROUP))
{
return true;
}
elsif (($strType eq 'path' || $strType eq 'file') &&
($strSubKey eq MANIFEST_SUBKEY_MODE))
{
return true;
}
elsif ($strType eq 'file' &&
($strSubKey eq MANIFEST_SUBKEY_CHECKSUM ||
$strSubKey eq MANIFEST_SUBKEY_FUTURE ||
$strSubKey eq MANIFEST_SUBKEY_MODIFICATION_TIME ||
$strSubKey eq MANIFEST_SUBKEY_REFERENCE ||
$strSubKey eq MANIFEST_SUBKEY_SIZE))
{
return true;
}
elsif ($strType eq 'link' &&
$strSubKey eq MANIFEST_SUBKEY_DESTINATION)
{
return true;
}
}
if ($strSection eq MANIFEST_SECTION_BACKUP)
{
if ($strKey eq MANIFEST_KEY_ARCHIVE_START ||
$strKey eq MANIFEST_KEY_ARCHIVE_STOP ||
$strKey eq MANIFEST_KEY_CHECKSUM ||
$strKey eq MANIFEST_KEY_FORMAT ||
$strKey eq MANIFEST_KEY_LABEL ||
$strKey eq MANIFEST_KEY_PRIOR ||
$strKey eq MANIFEST_KEY_REFERENCE ||
$strKey eq MANIFEST_KEY_TIMESTAMP_COPY_START ||
$strKey eq MANIFEST_KEY_TIMESTAMP_START ||
$strKey eq MANIFEST_KEY_TIMESTAMP_STOP ||
$strKey eq MANIFEST_KEY_TYPE ||
$strKey eq MANIFEST_KEY_VERSION)
{
return true;
}
}
elsif ($strSection eq MANIFEST_SECTION_BACKUP_OPTION)
{
if ($strKey eq MANIFEST_KEY_CHECKSUM ||
$strKey eq MANIFEST_KEY_COMPRESS ||
$strKey eq MANIFEST_KEY_HARDLINK)
{
return true;
}
}
elsif ($strSection eq MANIFEST_SECTION_BACKUP_TABLESPACE)
{
if ($strSubKey eq 'link' ||
$strSubKey eq 'path')
{
return true;
}
}
elsif ($strSection eq MANIFEST_SECTION_BACKUP_PATH)
{
if ($strKey eq 'base' || $strKey =~ /^tablespace\:.*$/)
{
return true;
}
}
confess &log(ASSERT, "manifest section '${strSection}', key '${strKey}'" .
(defined($strSubKey) ? ", subkey '$strSubKey'" : '') . ' is not valid');
}
####################################################################################################################################
# epoch
#
# Retrieves a value in the format YYYY-MM-DD HH24:MI:SS and converts to epoch time.
####################################################################################################################################
sub epoch
{
my $self = shift;
my $strSection = shift;
my $strKey = shift;
my $strSubKey = shift;
my $strValue = $self->get($strSection, $strKey, $strSubKey);
my ($iYear, $iMonth, $iDay, $iHour, $iMinute, $iSecond) = split(/[\s\-\:]+/, $strValue);
return timelocal($iSecond, $iMinute, $iHour, $iDay , $iMonth - 1, $iYear);
}
####################################################################################################################################
# KEYS
#
# Get a list of keys.
####################################################################################################################################
sub keys
{
my $self = shift;
my $strSection = shift;
my $strKey = shift;
if (defined($strSection))
{
if ($self->test($strSection, $strKey))
{
return sort(keys $self->get($strSection, $strKey));
}
return [];
}
return sort(keys $self->{oManifest});
}
####################################################################################################################################
# TEST
#
# Test a value to see if it equals the supplied test value. If no test value is given, tests that it is defined.
####################################################################################################################################
sub test
{
my $self = shift;
my $strSection = shift;
my $strValue = shift;
my $strSubValue = shift;
my $strTest = shift;
my $strResult = $self->get($strSection, $strValue, $strSubValue, false);
if (defined($strResult))
{
if (defined($strTest))
{
return $strResult eq $strTest ? true : false;
}
return true;
}
return false;
}
####################################################################################################################################
# BUILD
#
# Build the manifest object.
####################################################################################################################################
sub build
{
my $self = shift;
my $oFile = shift;
my $strDbClusterPath = shift;
my $oLastManifest = shift;
my $bNoStartStop = shift;
my $oTablespaceMapRef = shift;
my $strLevel = shift;
&log(DEBUG, 'Manifest->build');
# If no level is defined then it must be base
if (!defined($strLevel))
{
$strLevel = 'base';
if (defined($oLastManifest))
{
$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_PRIOR, undef,
$oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL));
}
# If bNoStartStop then build the tablespace map from pg_tblspc path
if ($bNoStartStop && !defined($oTablespaceMapRef))
{
$oTablespaceMapRef = {};
my %oTablespaceManifestHash;
$oFile->manifest(PATH_DB_ABSOLUTE, $strDbClusterPath . '/pg_tblspc', \%oTablespaceManifestHash);
foreach my $strName (sort(CORE::keys $oTablespaceManifestHash{name}))
{
if ($strName eq '.' or $strName eq '..')
{
next;
}
if ($oTablespaceManifestHash{name}{$strName}{type} ne 'l')
{
confess &log(ERROR, "pg_tblspc/${strName} is not a link");
}
&log(DEBUG, "Found tablespace ${strName}");
${$oTablespaceMapRef}{oid}{$strName}{name} = $strName;
}
}
}
# Get the manifest for this level
my %oManifestHash;
$oFile->manifest(PATH_DB_ABSOLUTE, $strDbClusterPath, \%oManifestHash);
$self->set(MANIFEST_SECTION_BACKUP_PATH, $strLevel, undef, $strDbClusterPath);
# Loop though all paths/files/links in the manifest
foreach my $strName (sort(CORE::keys $oManifestHash{name}))
{
# Skip certain files during backup
if (($strName =~ /^pg\_xlog\/.*/ && !$bNoStartStop) || # pg_xlog/ - this will be reconstructed
$strName =~ /^postmaster\.pid$/ || # postmaster.pid - to avoid confusing postgres when restoring
$strName =~ /^recovery\.conf$/) # recovery.conf - doesn't make sense to backup this file
{
next;
}
my $cType = $oManifestHash{name}{"${strName}"}{type};
my $strLinkDestination = $oManifestHash{name}{"${strName}"}{link_destination};
my $strSection = "${strLevel}:path";
if ($cType eq 'f')
{
$strSection = "${strLevel}:file";
}
elsif ($cType eq 'l')
{
$strSection = "${strLevel}:link";
}
elsif ($cType ne 'd')
{
confess &log(ASSERT, "unrecognized file type $cType for file $strName");
}
# User and group required for all types
$self->set($strSection, $strName, MANIFEST_SUBKEY_USER, $oManifestHash{name}{"${strName}"}{user});
$self->set($strSection, $strName, MANIFEST_SUBKEY_GROUP, $oManifestHash{name}{"${strName}"}{group});
# Mode for required file and path type only
if ($cType eq 'f' || $cType eq 'd')
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_MODE, $oManifestHash{name}{"${strName}"}{mode});
}
# Modification time and size required for file type only
if ($cType eq 'f')
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_MODIFICATION_TIME,
$oManifestHash{name}{"${strName}"}{modification_time} + 0);
$self->set($strSection, $strName, MANIFEST_SUBKEY_SIZE, $oManifestHash{name}{"${strName}"}{size} + 0);
}
# Link destination required for link type only
if ($cType eq 'l')
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_DESTINATION,
$oManifestHash{name}{"${strName}"}{link_destination});
# If this is a tablespace then follow the link
if (index($strName, 'pg_tblspc/') == 0 && $strLevel eq 'base')
{
my $strTablespaceOid = basename($strName);
my $strTablespaceName = ${$oTablespaceMapRef}{oid}{$strTablespaceOid}{name};
$self->set(MANIFEST_SECTION_BACKUP_TABLESPACE, $strTablespaceName,
MANIFEST_SUBKEY_LINK, $strTablespaceOid);
$self->set(MANIFEST_SECTION_BACKUP_TABLESPACE, $strTablespaceName,
MANIFEST_SUBKEY_PATH, $strLinkDestination);
$self->build($oFile, $strLinkDestination, $oLastManifest, $bNoStartStop, $oTablespaceMapRef,
"tablespace:${strTablespaceName}");
}
}
}
# If this is the base level then do post-processing
if ($strLevel eq 'base')
{
my $bTimeInFuture = false;
my $lTimeBegin = $oFile->wait(PATH_DB_ABSOLUTE);
# Loop through all backup paths (base and tablespaces)
foreach my $strPathKey ($self->keys(MANIFEST_SECTION_BACKUP_PATH))
{
my $strSection = "${strPathKey}:file";
# Make sure file section exists
if ($self->test($strSection))
{
# Loop though all files
foreach my $strName ($self->keys($strSection))
{
# If modification time is in the future (in this backup OR the last backup) set warning flag and do not
# allow a reference
if ($self->get($strSection, $strName, MANIFEST_SUBKEY_MODIFICATION_TIME) > $lTimeBegin ||
(defined($oLastManifest) && $oLastManifest->test($strSection, $strName, MANIFEST_SUBKEY_FUTURE, 'y')))
{
$bTimeInFuture = true;
# Only mark as future if still in the future in the current backup
if ($self->get($strSection, $strName, MANIFEST_SUBKEY_MODIFICATION_TIME) > $lTimeBegin)
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_FUTURE, 'y');
}
}
# Else check if modification time and size are unchanged since last backup
elsif (defined($oLastManifest) && $oLastManifest->test($strSection, $strName) &&
$self->get($strSection, $strName, MANIFEST_SUBKEY_SIZE) ==
$oLastManifest->get($strSection, $strName, MANIFEST_SUBKEY_SIZE) &&
$self->get($strSection, $strName, MANIFEST_SUBKEY_MODIFICATION_TIME) ==
$oLastManifest->get($strSection, $strName, MANIFEST_SUBKEY_MODIFICATION_TIME))
{
# Copy reference from previous backup if possible
if ($oLastManifest->test($strSection, $strName, MANIFEST_SUBKEY_REFERENCE))
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_REFERENCE,
$oLastManifest->get($strSection, $strName, MANIFEST_SUBKEY_REFERENCE));
}
# Otherwise the reference is to the previous backup
else
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_REFERENCE,
$oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL));
}
# Copy the checksum from previous manifest
if ($oLastManifest->test($strSection, $strName, MANIFEST_SUBKEY_CHECKSUM))
{
$self->set($strSection, $strName, MANIFEST_SUBKEY_CHECKSUM,
$oLastManifest->get($strSection, $strName, MANIFEST_SUBKEY_CHECKSUM));
}
# Build the manifest reference list - not used for processing but is useful for debugging
my $strFileReference = $self->get($strSection, $strName, MANIFEST_SUBKEY_REFERENCE);
my $strManifestReference = $self->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_REFERENCE,
undef, false);
if (!defined($strManifestReference))
{
$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_REFERENCE, undef, $strFileReference);
}
else
{
if ($strManifestReference !~ /^$strFileReference|,$strFileReference/)
{
$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_REFERENCE, undef,
$strManifestReference . ",${strFileReference}");
}
}
}
}
}
}
# Warn if any files in the current backup are in the future
if ($bTimeInFuture)
{
&log(WARN, "some files have timestamps in the future - they will be copied to prevent possible race conditions");
}
# Record the time when copying will start
$self->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START, undef,
timestamp_string_get(undef, $lTimeBegin + 1));
}
}
1;