diff --git a/lib/BackRest/Backup.pm b/lib/BackRest/Backup.pm index bb9526126..779d5f49f 100644 --- a/lib/BackRest/Backup.pm +++ b/lib/BackRest/Backup.pm @@ -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 diff --git a/lib/BackRest/Manifest.pm b/lib/BackRest/Manifest.pm new file mode 100644 index 000000000..f4611ce06 --- /dev/null +++ b/lib/BackRest/Manifest.pm @@ -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; diff --git a/lib/BackRest/Restore.pm b/lib/BackRest/Restore.pm index 21256edef..a38fd0c3d 100644 --- a/lib/BackRest/Restore.pm +++ b/lib/BackRest/Restore.pm @@ -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