1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-11-06 08:49:29 +02:00

Ownership code runs through - may not work for all cases.

This commit is contained in:
David Steele
2014-12-30 11:59:57 -05:00
parent 9f8f33f957
commit 46ba3a18df
4 changed files with 166 additions and 23 deletions

View File

@@ -4,7 +4,7 @@ PgBackRest aims to be a simple backup and restore system that can seamlessly sca
## release notes
### v0.50: ???
### v0.50: [under development]
* Added restore functionality.

View File

@@ -97,7 +97,9 @@ while ($strCommand ne OP_EXIT)
undef, param_get(\%oParamHash, 'destination_compress'),
undef, undef,
param_get(\%oParamHash, 'permission', false),
param_get(\%oParamHash, 'destination_path_create'));
param_get(\%oParamHash, 'destination_path_create'),
param_get(\%oParamHash, 'user', false),
param_get(\%oParamHash, 'group', false));
$oRemote->output_write();
}
@@ -143,7 +145,9 @@ while ($strCommand ne OP_EXIT)
param_get(\%oParamHash, 'ignore_missing_source', false),
undef,
param_get(\%oParamHash, 'permission', false),
param_get(\%oParamHash, 'destination_path_create')) ? 'Y' : 'N');
param_get(\%oParamHash, 'destination_path_create')) ? 'Y' : 'N',
param_get(\%oParamHash, 'user', false),
param_get(\%oParamHash, 'group', false));
}
# Generate a manifest
elsif ($strCommand eq OP_FILE_MANIFEST)

View File

@@ -578,7 +578,7 @@ sub path_create
my $self = shift;
my $strPathType = shift;
my $strPath = shift;
my $strPermission = shift;
my $strMode = shift;
my $bIgnoreExists = shift;
# Set operation variables
@@ -586,7 +586,7 @@ sub path_create
# Set operation and debug strings
my $strOperation = OP_FILE_PATH_CREATE;
my $strDebug = " ${strPathType}:${strPathOp}, permission " . (defined($strPermission) ? $strPermission : '[undef]');
my $strDebug = " ${strPathType}:${strPathOp}, permission " . (defined($strMode) ? $strMode : '[undef]');
&log(DEBUG, "${strOperation}: ${strDebug}");
if ($self->is_remote($strPathType))
@@ -596,9 +596,9 @@ sub path_create
$oParamHash{path} = ${strPathOp};
if (defined($strPermission))
if (defined($strMode))
{
$oParamHash{permission} = ${strPermission};
$oParamHash{permission} = ${strMode};
}
# Add remote info to debug string
@@ -616,9 +616,9 @@ sub path_create
# Attempt the create the directory
my $stryError;
if (defined($strPermission))
if (defined($strMode))
{
make_path($strPathOp, {mode => oct($strPermission), error => \$stryError});
make_path($strPathOp, {mode => oct($strMode), error => \$stryError});
}
else
{
@@ -1169,7 +1169,7 @@ sub manifest_recurse
# * source and destination can be local or remote
# * wire and output compression/decompression are supported
# * intermediate temp files are used to prevent partial copies
# * modification time and permissions can be set on destination file
# * modification time, mode, and ownership can be set on destination file
# * destination path can optionally be created
####################################################################################################################################
sub copy
@@ -1183,8 +1183,10 @@ sub copy
my $bDestinationCompress = shift;
my $bIgnoreMissingSource = shift;
my $lModificationTime = shift;
my $strPermission = shift;
my $strMode = shift;
my $bDestinationPathCreate = shift;
my $strUser = shift;
my $strGroup = shift;
# Set defaults
$bSourceCompressed = defined($bSourceCompressed) ? $bSourceCompressed : false;
@@ -1210,7 +1212,11 @@ sub copy
', source_compressed = ' . ($bSourceCompressed ? 'true' : 'false') .
', destination_compress = ' . ($bDestinationCompress ? 'true' : 'false') .
', ignore_missing_source = ' . ($bIgnoreMissingSource ? 'true' : 'false') .
', destination_path_create = ' . ($bDestinationPathCreate ? 'true' : 'false');
', destination_path_create = ' . ($bDestinationPathCreate ? 'true' : 'false') .
', modification_time = ' . (defined($lModificationTime) ? $lModificationTime : '[undef]') .
', mode = ' . (defined($strMode) ? $strMode : '[undef]') .
', user = ' . (defined($strUser) ? $strUser : '[undef]') .
', group = ' . (defined($strGroup) ? $strUser : '[undef]');
&log(DEBUG, OP_FILE_COPY . ": ${strDebug}");
# Open the source and destination files (if needed)
@@ -1331,9 +1337,19 @@ sub copy
$oParamHash{destination_compress} = $bDestinationCompress;
$oParamHash{destination_path_create} = $bDestinationPathCreate;
if (defined($strPermission))
if (defined($strMode))
{
$oParamHash{permission} = $strPermission;
$oParamHash{permission} = $strMode;
}
if (defined($strUser))
{
$oParamHash{user} = $strUser;
}
if (defined($strGroup))
{
$oParamHash{group} = $strGroup;
}
$hOut = $self->{oRemote}->{hIn};
@@ -1350,9 +1366,19 @@ sub copy
$oParamHash{destination_compress} = $bDestinationCompress;
$oParamHash{destination_path_create} = $bDestinationPathCreate;
if (defined($strPermission))
if (defined($strMode))
{
$oParamHash{permission} = $strPermission;
$oParamHash{permission} = $strMode;
}
if (defined($strUser))
{
$oParamHash{user} = $strUser;
}
if (defined($strGroup))
{
$oParamHash{group} = $strGroup;
}
if ($bIgnoreMissingSource)
@@ -1457,9 +1483,9 @@ sub copy
if (!$bDestinationRemote)
{
# Set the file permission if required
if (defined($strPermission))
if (defined($strMode))
{
chmod(oct($strPermission), $strDestinationTmpOp)
chmod(oct($strMode), $strDestinationTmpOp)
or confess &log(ERROR, "unable to set permissions for local ${strDestinationTmpOp}");
}
@@ -1470,6 +1496,20 @@ sub copy
or confess &log(ERROR, "unable to set time for local ${strDestinationTmpOp}");
}
# if user or group is defined
if (defined($strUser) || defined($strGroup))
{
chown(defined($strUser) ? getpwnam($strUser) : $<,
defined($strGroup) ? getgrnam($strGroup) : $(,
$strDestinationTmpOp);
}
if (defined($strMode))
{
chmod(oct($strMode), $strDestinationTmpOp)
or confess &log(ERROR, "unable to set permissions for local ${strDestinationTmpOp}");
}
# Move the file from tmp to final destination
$self->move(PATH_ABSOLUTE, $strDestinationTmpOp, PATH_ABSOLUTE, $strDestinationOp, true);
}

View File

@@ -55,6 +55,77 @@ sub new
return $self;
}
####################################################################################################################################
# MANIFEST_OWNERSHIP_TEST
#
# Checks the users and groups that exist in the manifest and emits warnings for ownership that cannot be set properly, either
# because the current user does not have permissions or because the user/group does not exist.
####################################################################################################################################
sub manifest_ownership_test
{
my $self = shift; # Class hash
my $oManifestRef = shift; # Backup manifest
# Create hashes to track valid/invalid users/groups
my %oOwnerHash = ();
# Create hash for each type and owner to be checked
my %oFileTypeHash = ('path' => true, 'link' => true, 'file' => true);
my %oOwnerTypeHash = ('user' => getpwuid($<), 'group' => getgrgid($());
# Loop through all backup paths
foreach my $strPathKey (sort(keys ${$oManifestRef}{'backup:path'}))
{
# Loop through owner types
foreach my $strOwnerType (sort (keys %oOwnerHash))
{
# Loop through types
foreach my $strFileType (sort (keys %oFileTypeHash))
{
# Get users and groups for paths
foreach my $strName (sort (keys ${$oManifestRef}{"${strPathKey}:${strFileType}"}))
{
my $strOwner = ${$oManifestRef}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType};
# If root then test to see if the user/group is valid
if ($< == 0)
{
# If the owner has not been tested yet then test it
if (!defined($oOwnerHash{$strOwnerType}{$strOwner}))
{
$oOwnerHash{$strOwnerType}{$strOwner} = ($strOwnerType eq 'user' && defined(getpwnam($strOwner))) ||
($strOwnerType eq 'group' && defined(getpwnam($strOwner)));
}
if (!$oOwnerHash{$strOwnerType}{$strOwner})
{
${$oManifestRef}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
$oOwnerTypeHash{$strOwnerType};
}
}
# Else set user/group to current user/group
else
{
if ($strOwner ne $oOwnerTypeHash{$strOwnerType})
{
$oOwnerHash{$strOwnerType}{$strOwner} = false;
${$oManifestRef}{"${strPathKey}:${strFileType}"}{$strName}{$strOwnerType} =
$oOwnerTypeHash{$strOwnerType};
}
}
}
}
# Output warning for any invalid owners
foreach my $strOwner (sort (keys $oOwnerHash{$strOwnerType}))
{
&log(WARN, "${strOwnerType} ${strOwner} " . ($< == 0 ? "does not exist" : "cannot be set") .
", changed to $oOwnerTypeHash{$strOwnerType}");
}
}
}
}
####################################################################################################################################
# MANIFEST_LOAD
#
@@ -120,6 +191,8 @@ sub manifest_load
{
confess &log(ERROR, 'backup ' . $self->{strBackupPath} . ' does not exist');
}
$self->manifest_ownership_test($oManifestRef);
}
####################################################################################################################################
@@ -359,16 +432,20 @@ sub restore_thread
# When a KILL signal is received, immediately abort
$SIG{'KILL'} = sub {threads->exit();};
# Get the current user and group to compare with stored permissions
my $strCurrentUser = getpwuid($<);
my $strCurrentGroup = getgrgid($();
# Loop through all the queues to restore files (exit when the original queue is reached
do
{
while (my $strMessage = ${$oyRestoreQueueRef}[$iQueueIdx]->dequeue())
{
my $strSourcePath = (split(/\|/, $strMessage))[0]; # Source path from backup
my $strSection = "${strSourcePath}:file"; # Backup section with file info
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
$strSourcePath =~ s/\:/\//g; # Replace : with / in source path
my $strName = (split(/\|/, $strMessage))[1]; # Name of file to be restored
$strSourcePath =~ s/\:/\//g; # Replace : with / in source path
my $strName = (split(/\|/, $strMessage))[1]; # Name of file to be restored
# Generate destination file name
my $strDestinationFile = $oFileThread->path_get(PATH_DB_ABSOLUTE, "${strDestinationPath}/${strName}");
@@ -387,6 +464,27 @@ sub restore_thread
$oFileThread->remove(PATH_DB_ABSOLUTE, $strDestinationFile);
}
# Set user and group if running as root (otherwise current user and group will be used for restore)
my $strUser = undef;
my $strGroup = undef;
if ($< == 0)
{
$strUser = ${$oManifestRef}{$strSection}{$strName}{user};
if (!defined(getpwnam($strUser)))
{
$strUser = $strCurrentUser;
}
$strGroup = ${$oManifestRef}{$strSection}{$strName}{group};
if (!defined(getgrnam($strGroup)))
{
$strGroup = $strCurrentGroup;
}
}
# Copy the file from the backup to the database
$oFileThread->copy(PATH_BACKUP_CLUSTER, $self->{strBackupPath} . "/${strSourcePath}/${strName}" .
($bSourceCompression ? '.' . $oFileThread->{strCompressExtension} : ''),
@@ -394,7 +492,8 @@ sub restore_thread
$bSourceCompression, # Source is compressed based on backup settings
undef, undef,
${$oManifestRef}{$strSection}{$strName}{modification_time},
${$oManifestRef}{$strSection}{$strName}{permission});
${$oManifestRef}{$strSection}{$strName}{permission},
$strUser, $strGroup);
}
# Even number threads move up when they have finished a queue, odd numbered threads move down