1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-14 10:13:05 +02:00

Integrating new manifest object.

This commit is contained in:
David Steele 2015-01-07 10:59:43 -05:00
parent d6d57e654e
commit 4f5ad8496d
3 changed files with 308 additions and 136 deletions

View File

@ -16,6 +16,7 @@ use Thread::Queue;
use lib dirname($0);
use BackRest::Utility;
use BackRest::Config;
use BackRest::Manifest;
use BackRest::File;
use BackRest::Db;
@ -811,7 +812,7 @@ sub backup_manifest_build
{
my $strDbClusterPath = shift;
my $oBackupManifestRef = shift;
my $oLastManifestRef = shift;
my $oLastManifest = shift;
my $oTablespaceMapRef = shift;
my $strLevel = shift;
@ -886,7 +887,7 @@ sub backup_manifest_build
${$oBackupManifestRef}{"backup:tablespace"}{"${strTablespaceName}"}{link} = $strTablespaceOid;
${$oBackupManifestRef}{"backup:tablespace"}{"${strTablespaceName}"}{path} = $strLinkDestination;
backup_manifest_build($strLinkDestination, $oBackupManifestRef, $oLastManifestRef,
backup_manifest_build($strLinkDestination, $oBackupManifestRef, undef,
$oTablespaceMapRef, "tablespace:${strTablespaceName}");
}
}
@ -899,73 +900,69 @@ sub backup_manifest_build
my $lTimeBegin = $oFile->wait(PATH_DB_ABSOLUTE);
# Only required if comparing to last backup
if (defined(${$oLastManifestRef}{backup}{label}))
# Loop through all backup paths (base and tablespaces)
foreach my $strPathKey (sort(keys ${$oBackupManifestRef}{'backup:path'}))
{
# Loop through all backup paths (base and tablespaces)
foreach my $strPathKey (sort(keys ${$oBackupManifestRef}{'backup:path'}))
my $strSection = "${strPathKey}:file";
# Make sure file section exists
if (defined(${$oBackupManifestRef}{$strSection}))
{
my $strSection = "${strPathKey}:file";
# Make sure file section exists
if (defined(${$oBackupManifestRef}{$strSection}))
# Loop though all files
foreach my $strName (sort (keys ${$oBackupManifestRef}{$strSection}))
{
# Loop though all files
foreach my $strName (sort (keys ${$oBackupManifestRef}{$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 (${$oBackupManifestRef}{$strSection}{$strName}{modification_time} > $lTimeBegin ||
(defined($oLastManifest) && $oLastManifest->test($strSection, $strName, MANIFEST_SUBVALUE_FUTURE, 'y')))
{
# If modification time is in the future (in this backup OR the last backup) set warning flag and do not
# allow a reference
if (${$oBackupManifestRef}{$strSection}{$strName}{modification_time} > $lTimeBegin ||
(defined(${$oLastManifestRef}{$strSection}{$strName}{future}) &&
${$oLastManifestRef}{$strSection}{$strName}{future} eq 'y'))
{
$bTimeInFuture = true;
$bTimeInFuture = true;
# Only mark as future if still in the future in the current backup
if (${$oBackupManifestRef}{$strSection}{$strName}{modification_time} > $lTimeBegin)
{
${$oBackupManifestRef}{$strSection}{$strName}{future} = 'y';
}
# Only mark as future if still in the future in the current backup
if (${$oBackupManifestRef}{$strSection}{$strName}{modification_time} > $lTimeBegin)
{
${$oBackupManifestRef}{$strSection}{$strName}{future} = 'y';
}
# Else check if modification time and size are unchanged
elsif (${$oBackupManifestRef}{$strSection}{$strName}{size} ==
${$oLastManifestRef}{$strSection}{$strName}{size} &&
${$oBackupManifestRef}{$strSection}{$strName}{modification_time} ==
${$oLastManifestRef}{$strSection}{$strName}{modification_time})
}
# Else check if modification time and size are unchanged since last backup
elsif (defined($oLastManifest) && $oLastManifest->test($strSection, $strName) &&
${$oBackupManifestRef}{$strSection}{$strName}{size} ==
$oLastManifest->get($strSection, $strName, MANIFEST_SUBVALUE_SIZE) &&
${$oBackupManifestRef}{$strSection}{$strName}{modification_time} ==
$oLastManifest->get($strSection, $strName, MANIFEST_SUBVALUE_MODIFICATION_TIME))
{
# Copy reference from previous backup if possible
if ($oLastManifest->test($strSection, $strName, MANIFEST_SUBVALUE_REFERENCE))
{
# Copy reference from previous backup if possible
if (defined(${$oLastManifestRef}{$strSection}{$strName}{reference}))
{
${$oBackupManifestRef}{$strSection}{$strName}{reference} =
${$oLastManifestRef}{$strSection}{$strName}{reference};
}
# Otherwise the reference is to the previous backup
else
{
${$oBackupManifestRef}{$strSection}{$strName}{reference} =
${$oLastManifestRef}{backup}{label};
}
${$oBackupManifestRef}{$strSection}{$strName}{reference} =
$oLastManifest->get($strSection, $strName, MANIFEST_SUBVALUE_REFERENCE);
}
# Otherwise the reference is to the previous backup
else
{
${$oBackupManifestRef}{$strSection}{$strName}{reference} =
$oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_VALUE_LABEL);
}
# Copy the checksum from previous manifest
if (defined(${$oLastManifestRef}{$strSection}{$strName}{checksum}))
{
${$oBackupManifestRef}{$strSection}{$strName}{checksum} =
${$oLastManifestRef}{$strSection}{$strName}{checksum};
}
# Copy the checksum from previous manifest
if ($oLastManifest->test($strSection, $strName, MANIFEST_SUBVALUE_CHECKSUM))
{
${$oBackupManifestRef}{$strSection}{$strName}{checksum} =
$oLastManifest->get($strSection, $strName, MANIFEST_SUBVALUE_CHECKSUM);
}
# Build the manifest reference list - not used for processing but is useful for debugging
my $strReference = ${$oBackupManifestRef}{$strSection}{$strName}{reference};
# Build the manifest reference list - not used for processing but is useful for debugging
my $strReference = ${$oBackupManifestRef}{$strSection}{$strName}{reference};
if (!defined(${$oBackupManifestRef}{backup}{reference}))
if (!defined(${$oBackupManifestRef}{backup}{reference}))
{
${$oBackupManifestRef}{backup}{reference} = $strReference;
}
else
{
if (${$oBackupManifestRef}{backup}{reference} !~ /^$strReference|,$strReference/)
{
${$oBackupManifestRef}{backup}{reference} = $strReference;
}
else
{
if (${$oBackupManifestRef}{backup}{reference} !~ /^$strReference|,$strReference/)
{
${$oBackupManifestRef}{backup}{reference} .= ",${strReference}";
}
${$oBackupManifestRef}{backup}{reference} .= ",${strReference}";
}
}
}
@ -1398,21 +1395,22 @@ sub backup
${oBackupManifest}{'backup:option'}{'checksum'} = !$bNoChecksum ? 'y' : 'n';
# Find the previous backup based on the type
my %oLastManifest = undef;
my $oLastManifest;
my $strBackupLastPath = backup_type_find($strType, $oFile->path_get(PATH_BACKUP_CLUSTER));
if (defined($strBackupLastPath))
{
ini_load($oFile->path_get(PATH_BACKUP_CLUSTER) . "/${strBackupLastPath}/backup.manifest", \%oLastManifest);
$oLastManifest = new BackRest::Manifest();
ini_load($oFile->path_get(PATH_BACKUP_CLUSTER) . "/${strBackupLastPath}/backup.manifest", $oLastManifest);
if (!defined($oLastManifest{backup}{label}))
if (!defined(${$oLastManifest}{backup}{label}))
{
confess &log(ERROR, "unable to find label in backup ${strBackupLastPath}");
}
&log(INFO, "last backup label: $oLastManifest{backup}{label}, version $oLastManifest{backup}{version}");
${oBackupManifest}{backup}{prior} = $oLastManifest{backup}{label};
&log(INFO, "last backup label: ${$oLastManifest}{backup}{label}, version ${$oLastManifest}{backup}{version}");
${oBackupManifest}{backup}{prior} = ${$oLastManifest}{backup}{label};
}
else
{
@ -1500,7 +1498,7 @@ sub backup
$oDb->tablespace_map_get(\%oTablespaceMap);
}
backup_manifest_build($strDbClusterPath, \%oBackupManifest, \%oLastManifest, \%oTablespaceMap);
backup_manifest_build($strDbClusterPath, \%oBackupManifest, $oLastManifest, \%oTablespaceMap);
&log(TEST, TEST_MANIFEST_BUILD);
# Check if an aborted backup exists for this stanza

167
lib/BackRest/Manifest.pm Normal file
View File

@ -0,0 +1,167 @@
####################################################################################################################################
# MANIFEST MODULE
####################################################################################################################################
package BackRest::Manifest;
use threads;
use strict;
use warnings;
use Carp;
use File::Basename qw(dirname);
use lib dirname($0);
use BackRest::Utility;
# Exports
use Exporter qw(import);
our @EXPORT = qw(MANIFEST_SECTION_BACKUP MANIFEST_SECTION_BACKUP_OPTION MANIFEST_SECTION_BACKUP_PATH
MANIFEST_VALUE_LABEL
MANIFEST_SUBVALUE_CHECKSUM MANIFEST_SUBVALUE_DESTINATION MANIFEST_SUBVALUE_FUTURE MANIFEST_SUBVALUE_GROUP
MANIFEST_SUBVALUE_MODE MANIFEST_SUBVALUE_MODIFICATION_TIME MANIFEST_SUBVALUE_REFERENCE MANIFEST_SUBVALUE_SIZE
MANIFEST_SUBVALUE_USER);
####################################################################################################################################
# MANIFEST Constants
####################################################################################################################################
use constant
{
MANIFEST_SECTION_BACKUP => 'backup',
MANIFEST_SECTION_BACKUP_OPTION => 'backup:option',
MANIFEST_SECTION_BACKUP_PATH => 'backup:path',
MANIFEST_VALUE_LABEL => 'label',
MANIFEST_SUBVALUE_CHECKSUM => 'checksum',
MANIFEST_SUBVALUE_DESTINATION => 'link_destination',
MANIFEST_SUBVALUE_FUTURE => 'future',
MANIFEST_SUBVALUE_GROUP => 'group',
MANIFEST_SUBVALUE_MODE => 'permission',
MANIFEST_SUBVALUE_MODIFICATION_TIME => 'modification_time',
MANIFEST_SUBVALUE_REFERENCE => 'reference',
MANIFEST_SUBVALUE_SIZE => 'size',
MANIFEST_SUBVALUE_USER => 'user'
};
####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
my $class = shift; # Class name
my $strFileName = shift; # Filename to load manifest from
# Create the class hash
my $self = {};
bless $self, $class;
return $self;
}
####################################################################################################################################
# GET
#
# Get a value.
####################################################################################################################################
sub get
{
my $self = shift;
my $strSection = shift;
my $strValue = shift;
my $strSubValue = shift;
my $bRequired = shift;
# 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;
if (defined($strSubValue))
{
if (!defined($strValue))
{
confess &log(ASSERT, 'subvalue requested bu value is not defined');
}
if (defined(${$self}{$strSection}{$strValue}))
{
$oResult = ${$self}{$strSection}{$strValue}{$strSubValue};
}
}
elsif (defined($strValue))
{
if (defined(${$self}{$strSection}))
{
$oResult = ${$self}{$strSection}{$strValue};
}
}
else
{
$oResult = ${$self}{$strSection};
}
if (!defined($oResult) && $bRequired)
{
confess &log(ASSERT, "manifest section '$strSection'" . (defined($strValue) ? ", value '$strValue'" : '') .
(defined($strSubValue) ? ", subvalue '$strSubValue'" : '') . ' is required but not defined');
}
return $oResult
}
####################################################################################################################################
# keys
#
# Get a list of keys.
####################################################################################################################################
sub keys
{
my $self = shift;
my $strSection = shift;
if ($self->test($strSection))
{
return sort(keys $self->get($strSection));
}
return [];
}
####################################################################################################################################
# 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;
}
1;

View File

@ -16,6 +16,7 @@ use lib dirname($0);
use BackRest::Utility;
use BackRest::ThreadGroup;
use BackRest::Config;
use BackRest::Manifest;
use BackRest::File;
####################################################################################################################################
@ -64,7 +65,7 @@ sub new
sub manifest_ownership_check
{
my $self = shift; # Class hash
my $oManifestRef = shift; # Backup manifest
my $oManifest = shift; # Backup manifest
# Create hashes to track valid/invalid users/groups
my %oOwnerHash = ();
@ -80,17 +81,17 @@ sub manifest_ownership_check
foreach my $strOwnerType (sort (keys %oOwnerTypeHash))
{
# Loop through all backup paths (base and tablespaces)
foreach my $strPathKey (sort(keys ${$oManifestRef}{'backup:path'}))
foreach my $strPathKey (sort(keys ${$oManifest}{'backup:path'}))
{
# Loop through types (path, link, file)
foreach my $strFileType (sort (keys %oFileTypeHash))
{
# Get users and groups for paths
if (defined(${$oManifestRef}{"${strPathKey}:${strFileType}"}))
if (defined(${$oManifest}{"${strPathKey}:${strFileType}"}))
{
foreach my $strName (sort (keys ${$oManifestRef}{"${strPathKey}:${strFileType}"}))
foreach my $strName (sort (keys ${$oManifest}{"${strPathKey}:${strFileType}"}))
{
my $strOwner = ${$oManifestRef}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType};
my $strOwner = ${$oManifest}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType};
# If root then test to see if the user/group is valid
if ($< == 0)
@ -114,7 +115,7 @@ sub manifest_ownership_check
if (!$oOwnerHash{$strOwnerType}{$strOwner})
{
${$oManifestRef}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
${$oManifest}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
$oOwnerTypeHash{$strOwnerType};
}
}
@ -124,7 +125,7 @@ sub manifest_ownership_check
if ($strOwner ne $oOwnerTypeHash{$strOwnerType})
{
$oOwnerHash{$strOwnerType}{$strOwner} = false;
${$oManifestRef}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
${$oManifest}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
$oOwnerTypeHash{$strOwnerType};
}
}
@ -156,7 +157,7 @@ sub manifest_ownership_check
sub manifest_load
{
my $self = shift; # Class hash
my $oManifestRef = shift; # Backup manifest
my $oManifest = shift; # Backup manifest
if ($self->{oFile}->exists(PATH_BACKUP_CLUSTER, $self->{strBackupPath}))
{
@ -165,7 +166,7 @@ sub manifest_load
PATH_DB_ABSOLUTE, $self->{strDbClusterPath} . '/' . FILE_MANIFEST);
# Load the manifest into a hash
ini_load($self->{oFile}->path_get(PATH_DB_ABSOLUTE, $self->{strDbClusterPath} . '/' . FILE_MANIFEST), $oManifestRef);
ini_load($self->{oFile}->path_get(PATH_DB_ABSOLUTE, $self->{strDbClusterPath} . '/' . FILE_MANIFEST), $oManifest);
# Remove the manifest now that it is in memory
$self->{oFile}->remove(PATH_DB_ABSOLUTE, $self->{strDbClusterPath} . '/' . FILE_MANIFEST);
@ -173,11 +174,11 @@ sub manifest_load
# If backup is latest then set it equal to backup label, else verify that requested backup and label match
if ($self->{strBackupPath} eq PATH_LATEST)
{
$self->{strBackupPath} = ${$oManifestRef}{'backup'}{label};
$self->{strBackupPath} = ${$oManifest}{'backup'}{label};
}
elsif ($self->{strBackupPath} ne ${$oManifestRef}{'backup'}{label})
elsif ($self->{strBackupPath} ne ${$oManifest}{'backup'}{label})
{
confess &log(ASSERT, "request backup $self->{strBackupPath} and label ${$oManifestRef}{'backup'}{label} do not match " .
confess &log(ASSERT, "request backup $self->{strBackupPath} and label ${$oManifest}{'backup'}{label} do not match " .
" - this indicates some sort of corruption (at the very least paths have been renamed.");
}
@ -191,7 +192,7 @@ sub manifest_load
if ($strPathKey eq 'base')
{
&log(INFO, "remapping base to ${strRemapPath}");
${$oManifestRef}{'backup:path'}{$strPathKey} = $strRemapPath;
${$oManifest}{'backup:path'}{$strPathKey} = $strRemapPath;
}
else
{
@ -203,7 +204,7 @@ sub manifest_load
}
# Make sure that the tablespace exists in the manifest
if (!defined(${$oManifestRef}{'backup:tablespace'}{$strPathKey}))
if (!defined(${$oManifest}{'backup:tablespace'}{$strPathKey}))
{
confess &log(ERROR, "cannot remap invalid tablespace ${strPathKey} to ${strRemapPath}");
}
@ -211,11 +212,11 @@ sub manifest_load
# Remap the tablespace in the manifest
&log(INFO, "remapping tablespace to ${strRemapPath}");
my $strTablespaceLink = ${$oManifestRef}{'backup:tablespace'}{$strPathKey}{link};
my $strTablespaceLink = ${$oManifest}{'backup:tablespace'}{$strPathKey}{link};
${$oManifestRef}{'backup:path'}{"tablespace:${strPathKey}"} = $strRemapPath;
${$oManifestRef}{'backup:tablespace'}{$strPathKey}{path} = $strRemapPath;
${$oManifestRef}{'base:link'}{"pg_tblspc/${strTablespaceLink}"}{link_destination} = $strRemapPath;
${$oManifest}{'backup:path'}{"tablespace:${strPathKey}"} = $strRemapPath;
${$oManifest}{'backup:tablespace'}{$strPathKey}{path} = $strRemapPath;
${$oManifest}{'base:link'}{"pg_tblspc/${strTablespaceLink}"}{link_destination} = $strRemapPath;
}
}
}
@ -225,7 +226,7 @@ sub manifest_load
confess &log(ERROR, 'backup ' . $self->{strBackupPath} . ' does not exist');
}
$self->manifest_ownership_check($oManifestRef);
$self->manifest_ownership_check($oManifest);
}
####################################################################################################################################
@ -237,16 +238,16 @@ sub manifest_load
sub clean
{
my $self = shift; # Class hash
my $oManifestRef = shift; # Backup manifest
my $oManifest = shift; # Backup manifest
# Track if files/links/paths where removed
my %oRemoveHash = ('file' => 0, 'path' => 0, 'link' => 0);
# Check each restore directory in the manifest and make sure that it exists and is empty.
# The --force option can be used to override the empty requirement.
foreach my $strPathKey (sort(keys ${$oManifestRef}{'backup:path'}))
foreach my $strPathKey ($oManifest->keys(MANIFEST_SECTION_BACKUP_PATH))
{
my $strPath = ${$oManifestRef}{'backup:path'}{$strPathKey};
my $strPath = $oManifest->get(MANIFEST_SECTION_BACKUP_PATH, $strPathKey);
&log(INFO, "checking/cleaning db path ${strPath}");
@ -287,22 +288,14 @@ sub clean
$strType = 'link';
}
# Build the section name
my $strSection = "${strPathKey}:${strType}";
# Check to see if the file/path/link exists in the manifest
if (defined(${$oManifestRef}{"${strPathKey}:${strType}"}{$strName}))
if ($oManifest->test($strSection, $strName))
{
my $strMode = ${$oManifestRef}{"${strPathKey}:${strType}"}{$strName}{permission};
# If file/path mode does not match, fix it
if ($strType ne 'link' && $strMode ne $oPathManifest{name}{$strName}{permission})
{
&log(DEBUG, "setting ${strFile} mode to ${strMode}");
chmod(oct($strMode), $strFile)
or confess 'unable to set mode ${strMode} for ${strFile}';
}
my $strUser = ${$oManifestRef}{"${strPathKey}:${strType}"}{$strName}{user};
my $strGroup = ${$oManifestRef}{"${strPathKey}:${strType}"}{$strName}{group};
my $strUser = $oManifest->get($strSection, $strName, MANIFEST_SUBVALUE_USER);
my $strGroup = $oManifest->get($strSection, $strName, MANIFEST_SUBVALUE_GROUP);
# If ownership does not match, fix it
if ($strUser ne $oPathManifest{name}{$strName}{user} ||
@ -311,14 +304,31 @@ sub clean
&log(DEBUG, "setting ${strFile} ownership to ${strUser}:${strGroup}");
# !!! Need to decide if it makes sense to set the user to anything other than the db owner
# !!! Follow what is done in file->copy (should create a new mode function?)
}
# If a link does not have the same destination, then delete it (it will be recreated later)
if ($strType eq 'link' && ${$oManifestRef}{"${strPathKey}:${strType}"}{$strName}{link_destination} ne
$oPathManifest{name}{$strName}{link_destination})
if ($strType eq 'link')
{
&log(DEBUG, "removing link ${strFile} - destination changed");
unlink($strFile) or confess &log(ERROR, "unable to delete file ${strFile}");
if ($strType eq 'link' && $oManifest->get($strSection, $strName, MANIFEST_SUBVALUE_DESTINATION) ne
$oPathManifest{name}{$strName}{link_destination})
{
&log(DEBUG, "removing link ${strFile} - destination changed");
unlink($strFile) or confess &log(ERROR, "unable to delete file ${strFile}");
}
}
# Else if file/path mode does not match, fix it
else
{
my $strMode = $oManifest->get($strSection, $strName, MANIFEST_SUBVALUE_MODE);
if ($strType ne 'link' && $strMode ne $oPathManifest{name}{$strName}{permission})
{
&log(DEBUG, "setting ${strFile} mode to ${strMode}");
chmod(oct($strMode), $strFile)
or confess 'unable to set mode ${strMode} for ${strFile}';
}
}
}
# If it does not then remove it
@ -359,16 +369,16 @@ sub clean
####################################################################################################################################
sub build
{
my $self = shift; # Class hash
my $oManifestRef = shift; # Backup manifest
my $self = shift; # Class hash
my $oManifest = shift; # Backup manifest
# Build paths/links in each restore path
foreach my $strPathKey (sort(keys ${$oManifestRef}{'backup:path'}))
foreach my $strPathKey ($oManifest->keys(MANIFEST_SECTION_BACKUP_PATH))
{
my $strPath = ${$oManifestRef}{'backup:path'}{$strPathKey};
my $strPath = $oManifest->get(MANIFEST_SECTION_BACKUP_PATH, $strPathKey);
# Create all paths in the manifest that do not already exist
foreach my $strName (sort (keys ${$oManifestRef}{"${strPathKey}:path"}))
foreach my $strName ($oManifest->keys("${strPathKey}:path"))
{
# Skip the root path
if ($strName eq '.')
@ -380,21 +390,18 @@ sub build
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, "${strPath}/${strName}"))
{
$self->{oFile}->path_create(PATH_DB_ABSOLUTE, "${strPath}/${strName}",
${$oManifestRef}{"${strPathKey}:path"}{$strName}{permission});
$oManifest->get("${strPathKey}:path", $strName, MANIFEST_SUBVALUE_MODE));
}
}
# Create all links in the manifest that do not already exist
if (defined(${$oManifestRef}{"${strPathKey}:link"}))
foreach my $strName ($oManifest->keys("${strPathKey}:link"))
{
foreach my $strName (sort (keys ${$oManifestRef}{"${strPathKey}:link"}))
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, "${strPath}/${strName}"))
{
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, "${strPath}/${strName}"))
{
$self->{oFile}->link_create(PATH_DB_ABSOLUTE,
${$oManifestRef}{"${strPathKey}:link"}{$strName}{link_destination},
PATH_DB_ABSOLUTE, "${strPath}/${strName}");
}
$self->{oFile}->link_create(PATH_DB_ABSOLUTE,
$oManifest->get("${strPathKey}:link", $strName, MANIFEST_SUBVALUE_DESTINATION),
PATH_DB_ABSOLUTE, "${strPath}/${strName}");
}
}
}
@ -419,25 +426,25 @@ sub restore
&log(INFO, "Restoring backup set " . $self->{strBackupPath});
# Make sure the backup path is valid and load the manifest
my %oManifest;
$self->manifest_load(\%oManifest);
my $oManifest = new BackRest::Manifest();
$self->manifest_load($oManifest);
# Clean the restore paths
$self->clean(\%oManifest);
$self->clean($oManifest);
# Build paths/links in the restore paths
$self->build(\%oManifest);
$self->build($oManifest);
# Assign the files in each path to a thread queue
my @oyRestoreQueue;
foreach my $strPathKey (sort(keys $oManifest{'backup:path'}))
foreach my $strPathKey (sort(keys ${$oManifest}{'backup:path'}))
{
if (defined($oManifest{"${strPathKey}:file"}))
if (defined(${$oManifest}{"${strPathKey}:file"}))
{
$oyRestoreQueue[@oyRestoreQueue] = Thread::Queue->new();
foreach my $strName (sort (keys $oManifest{"${strPathKey}:file"}))
foreach my $strName (sort (keys ${$oManifest}{"${strPathKey}:file"}))
{
$oyRestoreQueue[@oyRestoreQueue - 1]->enqueue("${strPathKey}|${strName}");
}
@ -451,7 +458,7 @@ sub restore
for (my $iThreadIdx = 0; $iThreadIdx < $self->{iThreadTotal}; $iThreadIdx++)
{
$oThreadGroup->add(threads->create(\&restore_thread, $self, $iThreadIdx, \@oyRestoreQueue, \%oManifest));
$oThreadGroup->add(threads->create(\&restore_thread, $self, $iThreadIdx, \@oyRestoreQueue, $oManifest));
}
$oThreadGroup->complete();
@ -467,7 +474,7 @@ sub restore_thread
my $self = shift; # Class hash
my $iThreadIdx = shift; # Defines the index of this thread
my $oyRestoreQueueRef = shift; # Restore queues
my $oManifestRef = shift; # Backup manifest
my $oManifest = shift; # Backup manifest
my $iDirection = $iThreadIdx % 2 == 0 ? 1 : -1; # Size of files currently copied by this thread
my $oFileThread = $self->{oFile}->clone($iThreadIdx); # Thread local file object
@ -477,7 +484,7 @@ sub restore_thread
my $iQueueIdx = $iQueueStartIdx;
# Set source compression
my $bSourceCompression = ${$oManifestRef}{'backup:option'}{compress} eq 'y' ? true : false;
my $bSourceCompression = ${$oManifest}{'backup:option'}{compress} eq 'y' ? true : false;
# When a KILL signal is received, immediately abort
$SIG{'KILL'} = sub {threads->exit();};
@ -493,19 +500,19 @@ sub restore_thread
{
my $strSourcePath = (split(/\|/, $strMessage))[0]; # Source path from backup
my $strSection = "${strSourcePath}:file"; # Backup section with file info
my $strDestinationPath = ${$oManifestRef}{'backup:path'}{$strSourcePath}; # Destination path stored in manifest
my $strDestinationPath = ${$oManifest}{'backup:path'}{$strSourcePath}; # Destination path stored in manifest
$strSourcePath =~ s/\:/\//g; # Replace : with / in source path
my $strName = (split(/\|/, $strMessage))[1]; # Name of file to be restored
# If the file is a reference to a previous backup and hardlinks are off, then fetch it from that backup
my $strReference = ${$oManifestRef}{'backup:option'}{hardlink} eq 'y' ? undef :
${$oManifestRef}{$strSection}{$strName}{reference};
my $strReference = ${$oManifest}{'backup:option'}{hardlink} eq 'y' ? undef :
${$oManifest}{$strSection}{$strName}{reference};
# Generate destination file name
my $strDestinationFile = $oFileThread->path_get(PATH_DB_ABSOLUTE, "${strDestinationPath}/${strName}");
# If checksum is set the destination file already exists, try a checksum before copying
my $strChecksum = ${$oManifestRef}{$strSection}{$strName}{checksum};
my $strChecksum = ${$oManifest}{$strSection}{$strName}{checksum};
if ($oFileThread->exists(PATH_DB_ABSOLUTE, $strDestinationFile))
{
@ -526,11 +533,11 @@ sub restore_thread
PATH_DB_ABSOLUTE, $strDestinationFile,
$bSourceCompression, # Source is compressed based on backup settings
undef, undef,
${$oManifestRef}{$strSection}{$strName}{modification_time},
${$oManifestRef}{$strSection}{$strName}{permission},
${$oManifest}{$strSection}{$strName}{modification_time},
${$oManifest}{$strSection}{$strName}{permission},
undef,
${$oManifestRef}{$strSection}{$strName}{user},
${$oManifestRef}{$strSection}{$strName}{group});
${$oManifest}{$strSection}{$strName}{user},
${$oManifest}{$strSection}{$strName}{group});
}
# Even number threads move up when they have finished a queue, odd numbered threads move down