You've already forked pgbackrest
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:
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user