1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-09-16 09:06:18 +02:00

Integration of new Manifest object.

This commit is contained in:
David Steele
2015-01-20 14:13:35 -05:00
parent 15a45f31f4
commit 8a7ee1bb1f
4 changed files with 131 additions and 88 deletions

View File

@@ -4,7 +4,7 @@ PgBackRest aims to be a simple backup and restore system that can seamlessly sca
Instead of relying on traditional backup tools like tar and rsync, PgBackRest implements all backup features internally and features a custom protocol for communicating with remote systems. Removing reliance on tar and rsync allows better solutions to database-specific backup issues. The custom remote protocol limits the types of connections that are required to perform a backup which increases security. Each thread requires only one SSH connection for remote backups.
A complete list of backrest features:
Primary PgBackRest features:
* Local or remote backup
* Multi-threaded backup/restore for performance

View File

@@ -1389,10 +1389,10 @@ sub backup
$oFile->path_create(PATH_BACKUP_CLUSTER, undef, undef, true);
# Declare the backup manifest
my %oBackupManifest;
my $oBackupManifest = new BackRest::Manifest();
${oBackupManifest}{'backup:option'}{'compress'} = $bCompress ? 'y' : 'n';
${oBackupManifest}{'backup:option'}{'checksum'} = !$bNoChecksum ? 'y' : 'n';
${$oBackupManifest->{oManifest}}{'backup:option'}{'compress'} = $bCompress ? 'y' : 'n';
${$oBackupManifest->{oManifest}}{'backup:option'}{'checksum'} = !$bNoChecksum ? 'y' : 'n';
# Find the previous backup based on the type
my $oLastManifest;
@@ -1401,16 +1401,15 @@ sub backup
if (defined($strBackupLastPath))
{
$oLastManifest = new BackRest::Manifest();
ini_load($oFile->path_get(PATH_BACKUP_CLUSTER) . "/${strBackupLastPath}/backup.manifest", $oLastManifest);
$oLastManifest = new BackRest::Manifest($oFile->path_get(PATH_BACKUP_CLUSTER) . "/${strBackupLastPath}/backup.manifest");
if (!defined(${$oLastManifest}{backup}{label}))
if (!defined(${$oLastManifest->{oManifest}}{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->{oManifest}}{backup}{label}, version ${$oLastManifest->{oManifest}}{backup}{version}");
${$oBackupManifest->{oManifest}}{backup}{prior} = ${$oLastManifest->{oManifest}}{backup}{label};
}
else
{
@@ -1426,11 +1425,11 @@ sub backup
$strType = BACKUP_TYPE_FULL;
}
${oBackupManifest}{backup}{type} = $strType;
${$oBackupManifest->{oManifest}}{backup}{type} = $strType;
if ($strType ne BACKUP_TYPE_FULL)
{
${oBackupManifest}{'backup:option'}{'hardlink'} = $bHardLink ? 'y' : 'n';
${$oBackupManifest->{oManifest}}{'backup:option'}{'hardlink'} = $bHardLink ? 'y' : 'n';
}
# Build backup tmp and config
@@ -1438,7 +1437,7 @@ sub backup
my $strBackupConfFile = $oFile->path_get(PATH_BACKUP_TMP, 'backup.manifest');
# Start backup (unless no-start-stop is set)
${oBackupManifest}{backup}{'timestamp-start'} = $strTimestampStart;
${$oBackupManifest->{oManifest}}{backup}{'timestamp-start'} = $strTimestampStart;
my $strArchiveStart;
if ($bNoStartStop)
@@ -1461,12 +1460,12 @@ sub backup
}
else
{
($strArchiveStart, ${oBackupManifest}{backup}{'timestamp-db-start'}) = $oDb->backup_start('pg_backrest backup started ' . $strTimestampStart, $bStartFast);
${oBackupManifest}{backup}{'archive-start'} = $strArchiveStart;
&log(INFO, 'archive start: ' . ${oBackupManifest}{backup}{'archive-start'});
($strArchiveStart, ${$oBackupManifest->{oManifest}}{backup}{'timestamp-db-start'}) = $oDb->backup_start('pg_backrest backup started ' . $strTimestampStart, $bStartFast);
${$oBackupManifest->{oManifest}}{backup}{'archive-start'} = $strArchiveStart;
&log(INFO, 'archive start: ' . ${$oBackupManifest->{oManifest}}{backup}{'archive-start'});
}
${oBackupManifest}{backup}{version} = version_get();
${$oBackupManifest->{oManifest}}{backup}{version} = version_get();
# Build the backup manifest
my %oTablespaceMap;
@@ -1498,7 +1497,7 @@ sub backup
$oDb->tablespace_map_get(\%oTablespaceMap);
}
backup_manifest_build($strDbClusterPath, \%oBackupManifest, $oLastManifest, \%oTablespaceMap);
backup_manifest_build($strDbClusterPath, $oBackupManifest->{oManifest}, $oLastManifest, \%oTablespaceMap);
&log(TEST, TEST_MANIFEST_BUILD);
# Check if an aborted backup exists for this stanza
@@ -1526,13 +1525,13 @@ sub backup
# The backup is usable if between the current backup and the aborted backup:
# 1) The version matches
# 2) The type of both is full or the types match and prior matches
if ($strAbortedVersion eq $oBackupManifest{backup}{version})
if ($strAbortedVersion eq ${$oBackupManifest->{oManifest}}{backup}{version})
{
if ($strAbortedType eq BACKUP_TYPE_FULL && $oBackupManifest{backup}{type} eq BACKUP_TYPE_FULL)
if ($strAbortedType eq BACKUP_TYPE_FULL && ${$oBackupManifest->{oManifest}}{backup}{type} eq BACKUP_TYPE_FULL)
{
$bUsable = true;
}
elsif ($strAbortedType eq $oBackupManifest{backup}{type} && $strAbortedPrior eq $oBackupManifest{backup}{prior})
elsif ($strAbortedType eq ${$oBackupManifest->{oManifest}}{backup}{type} && $strAbortedPrior eq ${$oBackupManifest->{oManifest}}{backup}{prior})
{
$bUsable = true;
}
@@ -1545,7 +1544,7 @@ sub backup
&log(WARN, 'aborted backup of same type exists, will be cleaned to remove invalid files and resumed');
# Clean the old backup tmp path
backup_tmp_clean(\%oBackupManifest);
backup_tmp_clean($oBackupManifest->{oManifest});
}
# Else remove it
else
@@ -1571,19 +1570,19 @@ sub backup
close($hVersionFile);
# Save the backup conf file with the manifest
ini_save($strBackupConfFile, \%oBackupManifest);
ini_save($strBackupConfFile, $oBackupManifest->{oManifest});
# Perform the backup
backup_file($strDbClusterPath, \%oBackupManifest);
backup_file($strDbClusterPath, $oBackupManifest->{oManifest});
# Stop backup (unless no-start-stop is set)
my $strArchiveStop;
if (!$bNoStartStop)
{
($strArchiveStop, ${oBackupManifest}{backup}{'timestamp-db-stop'}) = $oDb->backup_stop();
${oBackupManifest}{backup}{'archive-stop'} = $strArchiveStop;
&log(INFO, 'archive stop: ' . ${oBackupManifest}{backup}{'archive-stop'});
($strArchiveStop, ${$oBackupManifest->{oManifest}}{backup}{'timestamp-db-stop'}) = $oDb->backup_stop();
${$oBackupManifest->{oManifest}}{backup}{'archive-stop'} = $strArchiveStop;
&log(INFO, 'archive stop: ' . ${$oBackupManifest->{oManifest}}{backup}{'archive-stop'});
}
# If archive logs are required to complete the backup, then fetch them. This is the default, but can be overridden if the
@@ -1592,7 +1591,7 @@ sub backup
if ($bArchiveRequired)
{
# Save the backup conf file second time - before getting archive logs in case that fails
ini_save($strBackupConfFile, \%oBackupManifest);
ini_save($strBackupConfFile, $oBackupManifest->{oManifest});
# After the backup has been stopped, need to make a copy of the archive logs need to make the db consistent
&log(DEBUG, "retrieving archive logs ${strArchiveStart}:${strArchiveStop}");
@@ -1646,11 +1645,11 @@ sub backup
}
# Record timestamp stop in the config
${oBackupManifest}{backup}{'timestamp-stop'} = timestamp_string_get();
${oBackupManifest}{backup}{label} = $strBackupPath;
${$oBackupManifest->{oManifest}}{backup}{'timestamp-stop'} = timestamp_string_get();
${$oBackupManifest->{oManifest}}{backup}{label} = $strBackupPath;
# Save the backup conf file final time
ini_save($strBackupConfFile, \%oBackupManifest);
ini_save($strBackupConfFile, $oBackupManifest->{oManifest});
&log(INFO, "new backup label: ${strBackupPath}");

View File

@@ -19,7 +19,7 @@ use Exporter qw(import);
our @EXPORT = qw(MANIFEST_SECTION_BACKUP MANIFEST_SECTION_BACKUP_OPTION MANIFEST_SECTION_BACKUP_PATH
MANIFEST_SECTION_BACKUP_TABLESPACE
MANIFEST_KEY_LABEL MANIFEST_KEY_TIMESTAMP_COPY_START
MANIFEST_KEY_COMPRESS MANIFEST_KEY_HARDLINK MANIFEST_KEY_LABEL MANIFEST_KEY_TIMESTAMP_COPY_START
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
@@ -35,6 +35,8 @@ use constant
MANIFEST_SECTION_BACKUP_PATH => 'backup:path',
MANIFEST_SECTION_BACKUP_TABLESPACE => 'backup:tablespace',
MANIFEST_KEY_COMPRESS => 'compress',
MANIFEST_KEY_HARDLINK => 'hardlink',
MANIFEST_KEY_LABEL => 'label',
MANIFEST_KEY_TIMESTAMP_COPY_START => 'timestamp-copy-start',
@@ -63,9 +65,34 @@ sub new
my $self = {};
bless $self, $class;
# Create the manifest hash
$self->{oManifest} = {};
# Load the manifest if a filename is provided
if (defined($strFileName))
{
ini_load($strFileName, $self->{oManifest});
}
return $self;
}
####################################################################################################################################
# SAVE
#
# Save the config file.
####################################################################################################################################
# sub save
# {
# my $self = shift;
# my $strFileName = shift; # Filename to save manifest to
#
# # Save the config file
# ini_save($strFileName, $self);
#
# return $self;
# }
####################################################################################################################################
# GET
#
@@ -79,6 +106,8 @@ sub get
my $strSubValue = shift;
my $bRequired = shift;
my $oManifest = $self->{oManifest};
# Section must always be defined
if (!defined($strSection))
{
@@ -89,7 +118,7 @@ sub get
$bRequired = defined($bRequired) ? $bRequired : true;
# Store the result
my $oResult;
my $oResult = undef;
if (defined($strSubValue))
{
@@ -98,21 +127,21 @@ sub get
confess &log(ASSERT, 'subvalue requested bu value is not defined');
}
if (defined(${$self}{$strSection}{$strValue}))
if (defined(${$oManifest}{$strSection}{$strValue}))
{
$oResult = ${$self}{$strSection}{$strValue}{$strSubValue};
$oResult = ${$oManifest}{$strSection}{$strValue}{$strSubValue};
}
}
elsif (defined($strValue))
{
if (defined(${$self}{$strSection}))
if (defined(${$oManifest}{$strSection}))
{
$oResult = ${$self}{$strSection}{$strValue};
$oResult = ${$oManifest}{$strSection}{$strValue};
}
}
else
{
$oResult = ${$self}{$strSection};
$oResult = ${$oManifest}{$strSection};
}
if (!defined($oResult) && $bRequired)
@@ -137,16 +166,18 @@ sub set
my $strSubKey = shift;
my $strValue = shift;
my $oManifest = $self->{oManifest};
# Make sure the keys are valid
$self->valid($strSection, $strSubKey, $strValue);
if (defined($strSubKey))
{
${$self}{$strSection}{$strKey}{$strSubKey} = $strValue;
${$oManifest}{$strSection}{$strKey}{$strSubKey} = $strValue;
}
else
{
${$self}{$strSection}{$strKey} = $strValue;
${$oManifest}{$strSection}{$strKey} = $strValue;
}
}
@@ -214,6 +245,8 @@ sub keys
return sort(keys $self->get($strSection));
}
confess 'nothing was returned';
return [];
}

View File

@@ -78,23 +78,25 @@ sub manifest_ownership_check
my $strDefaultGroup = getgrgid($();
my %oFileTypeHash = ('path' => true, 'link' => true, 'file' => true);
my %oOwnerTypeHash = ('user' => $strDefaultUser, 'group' => $strDefaultGroup);
my %oOwnerTypeHash = (&MANIFEST_SUBKEY_USER => $strDefaultUser, &MANIFEST_SUBKEY_GROUP => $strDefaultGroup);
# Loop through owner types (user, group)
foreach my $strOwnerType (sort (keys %oOwnerTypeHash))
{
# Loop through all backup paths (base and tablespaces)
foreach my $strPathKey (sort(keys ${$oManifest}{'backup:path'}))
foreach my $strPathKey ($oManifest->keys(MANIFEST_SECTION_BACKUP_PATH))
{
# Loop through types (path, link, file)
foreach my $strFileType (sort (keys %oFileTypeHash))
{
my $strSection = "${strPathKey}:${strFileType}";
# Get users and groups for paths
if (defined(${$oManifest}{"${strPathKey}:${strFileType}"}))
if ($oManifest->test($strSection))
{
foreach my $strName (sort (keys ${$oManifest}{"${strPathKey}:${strFileType}"}))
foreach my $strName ($oManifest->keys($strSection))
{
my $strOwner = ${$oManifest}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType};
my $strOwner = $oManifest->get($strSection, $strName, $strOwnerType);
# If root then test to see if the user/group is valid
if ($< == 0)
@@ -118,8 +120,7 @@ sub manifest_ownership_check
if (!$oOwnerHash{$strOwnerType}{$strOwner})
{
${$oManifest}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
$oOwnerTypeHash{$strOwnerType};
$oManifest->set($strSection, $strName, $strOwnerType, $oOwnerTypeHash{$strOwnerType});
}
}
# Else set user/group to current user/group
@@ -128,8 +129,7 @@ sub manifest_ownership_check
if ($strOwner ne $oOwnerTypeHash{$strOwnerType})
{
$oOwnerHash{$strOwnerType}{$strOwner} = false;
${$oManifest}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
$oOwnerTypeHash{$strOwnerType};
$oManifest->set($strSection, $strName, $strOwnerType, $oOwnerTypeHash{$strOwnerType});
}
}
}
@@ -160,7 +160,6 @@ sub manifest_ownership_check
sub manifest_load
{
my $self = shift; # Class hash
my $oManifest = shift; # Backup manifest
if ($self->{oFile}->exists(PATH_BACKUP_CLUSTER, $self->{strBackupPath}))
{
@@ -169,20 +168,23 @@ 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), $oManifest);
my $oManifest = new BackRest::Manifest($self->{oFile}->path_get(PATH_DB_ABSOLUTE,
$self->{strDbClusterPath} . '/' . FILE_MANIFEST));
# Remove the manifest now that it is in memory
$self->{oFile}->remove(PATH_DB_ABSOLUTE, $self->{strDbClusterPath} . '/' . FILE_MANIFEST);
# If backup is latest then set it equal to backup label, else verify that requested backup and label match
my $strBackupLabel = $oManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL);
if ($self->{strBackupPath} eq PATH_LATEST)
{
$self->{strBackupPath} = ${$oManifest}{'backup'}{label};
$self->{strBackupPath} = $strBackupLabel;
}
elsif ($self->{strBackupPath} ne ${$oManifest}{'backup'}{label})
elsif ($self->{strBackupPath} ne $strBackupLabel)
{
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.");
confess &log(ASSERT, "request backup $self->{strBackupPath} and label ${strBackupLabel} do not match " .
' - this indicates some sort of corruption (at the very least paths have been renamed.');
}
# If tablespaces have been remapped, update the manifest
@@ -195,7 +197,7 @@ sub manifest_load
if ($strPathKey eq 'base')
{
&log(INFO, "remapping base to ${strRemapPath}");
$oManifest->set(MANIFEST_SECTION_BACKUP_PATH, $strPathKey) = $strRemapPath;
$oManifest->set(MANIFEST_SECTION_BACKUP_PATH, $strPathKey, undef, $strRemapPath);
}
else
{
@@ -223,13 +225,13 @@ sub manifest_load
}
}
}
}
else
{
confess &log(ERROR, 'backup ' . $self->{strBackupPath} . ' does not exist');
}
$self->manifest_ownership_check($oManifest);
return $oManifest;
}
confess &log(ERROR, 'backup ' . $self->{strBackupPath} . ' does not exist');
}
####################################################################################################################################
@@ -375,12 +377,14 @@ sub build
my $oManifest = shift; # Backup manifest
# Build paths/links in each restore path
foreach my $strPathKey ($oManifest->keys(MANIFEST_SECTION_BACKUP_PATH))
foreach my $strSectionPathKey ($oManifest->keys(MANIFEST_SECTION_BACKUP_PATH))
{
my $strPath = $oManifest->get(MANIFEST_SECTION_BACKUP_PATH, $strPathKey);
my $strSectionPath = $oManifest->get(MANIFEST_SECTION_BACKUP_PATH, $strSectionPathKey);
# Create all paths in the manifest that do not already exist
foreach my $strName ($oManifest->keys("${strPathKey}:path"))
my $strSection = "${strSectionPathKey}:path";
foreach my $strName ($oManifest->keys($strSection))
{
# Skip the root path
if ($strName eq '.')
@@ -389,21 +393,27 @@ sub build
}
# Create the Path
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, "${strPath}/${strName}"))
my $strPath = "${strSectionPath}/${strName}";
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, $strPath))
{
$self->{oFile}->path_create(PATH_DB_ABSOLUTE, "${strPath}/${strName}",
$oManifest->get("${strPathKey}:path", $strName, MANIFEST_SUBKEY_MODE));
$self->{oFile}->path_create(PATH_DB_ABSOLUTE, $strPath,
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_MODE));
}
}
# Create all links in the manifest that do not already exist
foreach my $strName ($oManifest->keys("${strPathKey}:link"))
$strSection = "${strSectionPathKey}:link";
foreach my $strName ($oManifest->keys($strSection))
{
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, "${strPath}/${strName}"))
my $strLink = "${strSectionPath}/${strName}";
if (!$self->{oFile}->exists(PATH_DB_ABSOLUTE, $strLink))
{
$self->{oFile}->link_create(PATH_DB_ABSOLUTE,
$oManifest->get("${strPathKey}:link", $strName, MANIFEST_SUBKEY_DESTINATION),
PATH_DB_ABSOLUTE, "${strPath}/${strName}");
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_DESTINATION),
PATH_DB_ABSOLUTE, $strLink);
}
}
}
@@ -428,8 +438,7 @@ sub restore
&log(INFO, "Restoring backup set " . $self->{strBackupPath});
# Make sure the backup path is valid and load the manifest
my $oManifest = new BackRest::Manifest();
$self->manifest_load($oManifest);
my $oManifest = $self->manifest_load();
# Clean the restore paths
$self->clean($oManifest);
@@ -440,13 +449,15 @@ sub restore
# Assign the files in each path to a thread queue
my @oyRestoreQueue;
foreach my $strPathKey (sort(keys ${$oManifest}{'backup:path'}))
foreach my $strPathKey ($oManifest->keys(MANIFEST_SECTION_BACKUP_PATH))
{
if (defined(${$oManifest}{"${strPathKey}:file"}))
my $strSection = "${strPathKey}:file";
if ($oManifest->test($strSection))
{
$oyRestoreQueue[@oyRestoreQueue] = Thread::Queue->new();
foreach my $strName (sort (keys ${$oManifest}{"${strPathKey}:file"}))
foreach my $strName ($oManifest->keys($strSection))
{
$oyRestoreQueue[@oyRestoreQueue - 1]->enqueue("${strPathKey}|${strName}");
}
@@ -489,7 +500,7 @@ sub restore_thread
my $lCopyTimeBegin = $oManifest->epoch(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START);
# Set source compression
my $bSourceCompression = ${$oManifest}{'backup:option'}{compress} eq 'y' ? true : false;
my $bSourceCompression = $oManifest->get(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS) eq 'y' ? true : false;
# When a KILL signal is received, immediately abort
$SIG{'KILL'} = sub {threads->exit();};
@@ -505,26 +516,26 @@ sub restore_thread
{
my $strSourcePath = (split(/\|/, $strMessage))[0]; # Source path from backup
my $strSection = "${strSourcePath}:file"; # Backup section with file info
my $strDestinationPath = ${$oManifest}{'backup:path'}{$strSourcePath}; # Destination path stored in manifest
my $strDestinationPath = $oManifest->get(MANIFEST_SECTION_BACKUP_PATH, # Destination path stored in manifest
$strSourcePath);
$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 = defined(${$oManifest}{'backup:option'}{hardlink}) && ${$oManifest}{'backup:option'}{hardlink} eq 'y' ? undef :
${$oManifest}{$strSection}{$strName}{reference};
my $strReference = $oManifest->test(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_HARDLINK, undef, 'y') ? undef :
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_REFERENCE, false);
# 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 = ${$oManifest}{$strSection}{$strName}{checksum};
if ($oFileThread->exists(PATH_DB_ABSOLUTE, $strDestinationFile))
{
# Perform delta if requested
if ($self->{bDelta})
{
# Do checksum delta if --force was not requested and checksums exist
my $strChecksum = $oManifest->get($strSection, $strName, MANIFEST_SUBKEY_CHECKSUM, false);
if (!$self->{bForce} && defined($strChecksum) &&
$oFileThread->hash(PATH_DB_ABSOLUTE, $strDestinationFile) eq $strChecksum)
{
@@ -560,11 +571,11 @@ sub restore_thread
PATH_DB_ABSOLUTE, $strDestinationFile,
$bSourceCompression, # Source is compressed based on backup settings
undef, undef,
${$oManifest}{$strSection}{$strName}{modification_time},
${$oManifest}{$strSection}{$strName}{permission},
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_MODIFICATION_TIME),
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_MODE),
undef,
${$oManifest}{$strSection}{$strName}{user},
${$oManifest}{$strSection}{$strName}{group});
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_USER),
$oManifest->get($strSection, $strName, MANIFEST_SUBKEY_GROUP));
}
# Even number threads move up when they have finished a queue, odd numbered threads move down