Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  003_corruption.pl

  Sprache: Shell
 


# Copyright (c) 2021-2025, PostgreSQL Global Development Group

# Verify that various forms of corruption are detected by pg_verifybackup.

use strict;
use warnings FATAL => 'all';
use Cwd;
use File::Path qw(rmtree);
use File::Copy;
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;

my $tar = $ENV{TAR};
my @tar_p_flags = tar_portability_options($tar);

my $primary = PostgreSQL::Test::Cluster->new('primary');
$primary->init(allows_streaming => 1);
$primary->start;

# Include a user-defined tablespace in the hopes of detecting problems in that
# area.
my $source_ts_path = PostgreSQL::Test::Utils::tempdir_short();
my $source_ts_prefix = $source_ts_path;
$source_ts_prefix =~ s!(^[A-Z]:/[^/]*)/.*!$1!;

$primary->safe_psql('postgres', <<EOM);
CREATE TABLE x1 (a int);
INSERT INTO x1 VALUES (111);
CREATE TABLESPACE ts1 LOCATION '$source_ts_path';
CREATE TABLE x2 (a int) TABLESPACE ts1;
INSERT INTO x1 VALUES (222);
EOM

my @scenario = (
 {
  'name' => 'extra_file',
  'mutilate' => \&mutilate_extra_file,
  'fails_like' =>
    qr/extra_file.*present (on disk|in archive "[^"]+") but not in the manifest/
 },
 {
  'name' => 'extra_tablespace_file',
  'mutilate' => \&mutilate_extra_tablespace_file,
  'fails_like' =>
    qr/extra_ts_file.*present (on disk|in archive "[^"]+") but not in the manifest/
 },
 {
  'name' => 'missing_file',
  'mutilate' => \&mutilate_missing_file,
  'fails_like' =>
    qr/pg_xact\/0000.*present in the manifest but not (on disk|in archive "[^"]+")/
 },
 {
  'name' => 'missing_tablespace',
  'mutilate' => \&mutilate_missing_tablespace,
  'fails_like' =>
    qr/pg_tblspc.*present in the manifest but not (on disk|in archive "[^"]+")/
 },
 {
  'name' => 'append_to_file',
  'mutilate' => \&mutilate_append_to_file,
  'fails_like' =>
    qr/has size \d+ (on disk|in archive "[^"]+") but size \d+ in the manifest/
 },
 {
  'name' => 'truncate_file',
  'mutilate' => \&mutilate_truncate_file,
  'fails_like' =>
    qr/has size 0 (on disk|in archive "[^"]+") but size \d+ in the manifest/
 },
 {
  'name' => 'replace_file',
  'mutilate' => \&mutilate_replace_file,
  'fails_like' => qr/checksum mismatch for file/
 },
 {
  'name' => 'system_identifier',
  'mutilate' => \&mutilate_system_identifier,
  'fails_like' =>
    qr/manifest system identifier is .*, but control file has/
 },
 {
  'name' => 'bad_manifest',
  'mutilate' => \&mutilate_bad_manifest,
  'fails_like' => qr/manifest checksum mismatch/
 },
 {
  'name' => 'open_file_fails',
  'mutilate' => \&mutilate_open_file_fails,
  'fails_like' => qr/could not open file/,
  'needs_unix_permissions' => 1
 },
 {
  'name' => 'open_directory_fails',
  'mutilate' => \&mutilate_open_directory_fails,
  'cleanup' => \&cleanup_open_directory_fails,
  'fails_like' => qr/could not open directory/,
  'needs_unix_permissions' => 1
 },
 {
  'name' => 'search_directory_fails',
  'mutilate' => \&mutilate_search_directory_fails,
  'cleanup' => \&cleanup_search_directory_fails,
  'fails_like' => qr/could not stat file or directory/,
  'needs_unix_permissions' => 1
 });

for my $scenario (@scenario)
{
 my $name = $scenario->{'name'};

  SKIP:
 {
  skip "unix-style permissions not supported on Windows"4
    if ($scenario->{'needs_unix_permissions'}
   && ($windows_os || $Config::Config{osname} eq 'cygwin'));

  # Take a backup and check that it verifies OK.
  my $backup_path = $primary->backup_dir . '/' . $name;
  my $backup_ts_path = PostgreSQL::Test::Utils::tempdir_short();
  # The tablespace map parameter confuses Msys2, which tries to mangle
  # it. Tell it not to.
  # See https://www.msys2.org/wiki/Porting/#filesystem-namespaces
  local $ENV{MSYS2_ARG_CONV_EXCL} = $source_ts_prefix;
  $primary->command_ok(
   [
    'pg_basebackup',
    '--pgdata' => $backup_path,
    '--no-sync',
    '--checkpoint' => 'fast',
    '--tablespace-mapping' =>
      "${source_ts_path}=${backup_ts_path}",
   ],
   "base backup ok");
  command_ok([ 'pg_verifybackup', $backup_path ],
   "intact backup verified");

  # Mutilate the backup in some way.
  $scenario->{'mutilate'}->($backup_path);

  # Now check that the backup no longer verifies.
  command_fails_like(
   [ 'pg_verifybackup', $backup_path ],
   $scenario->{'fails_like'},
   "corrupt backup fails verification: $name");

  # Run cleanup hook, if provided.
  $scenario->{'cleanup'}->($backup_path)
    if exists $scenario->{'cleanup'};

  # Turn it into a tar-format backup and see if we can still detect the
  # same problem, unless the scenario needs UNIX permissions or we don't
  # have a TAR program available. Note that this destructively modifies
  # the backup directory.
  if (   !$scenario->{'needs_unix_permissions'}
   && defined $tar
   && $tar ne '')
  {
   my $tar_backup_path = $primary->backup_dir . '/tar_' . $name;
   mkdir($tar_backup_path) || die "mkdir $tar_backup_path: $!";

   # tar and then remove each tablespace. We remove the original files
   # so that they don't also end up in base.tar.
   my @tsoid = grep { $_ ne '.' && $_ ne '..' }
     slurp_dir("$backup_path/pg_tblspc");
   my $cwd = getcwd;
   for my $tsoid (@tsoid)
   {
    my $tspath = $backup_path . '/pg_tblspc/' . $tsoid;

    chdir($tspath) || die "chdir: $!";
    command_ok(
     [
      $tar, @tar_p_flags,
      '-cf' => "$tar_backup_path/$tsoid.tar",
      '.'
     ]);
    chdir($cwd) || die "chdir: $!";
    rmtree($tspath);
   }

   # tar and remove pg_wal
   chdir($backup_path . '/pg_wal') || die "chdir: $!";
   command_ok(
    [
     $tar, @tar_p_flags,
     '-cf' => "$tar_backup_path/pg_wal.tar",
     '.'
    ]);
   chdir($cwd) || die "chdir: $!";
   rmtree($backup_path . '/pg_wal');

   # move the backup manifest
   move(
    $backup_path . '/backup_manifest',
    $tar_backup_path . '/backup_manifest'
   ) or die "could not copy manifest to $tar_backup_path";

   # Construct base.tar with what's left.
   chdir($backup_path) || die "chdir: $!";
   command_ok(
    [
     $tar, @tar_p_flags,
     '-cf' => "$tar_backup_path/base.tar",
     '.'
    ]);
   chdir($cwd) || die "chdir: $!";

   # Now check that the backup no longer verifies. We must use -n
   # here, because pg_waldump can't yet read WAL from a tarfile.
   command_fails_like(
    [ 'pg_verifybackup''--no-parse-wal', $tar_backup_path ],
    $scenario->{'fails_like'},
    "corrupt backup fails verification: $name");

   # Use rmtree to reclaim space.
   rmtree($tar_backup_path);
  }

  # Use rmtree to reclaim space.
  rmtree($backup_path);
 }
}

sub create_extra_file
{
 my ($backup_path, $relative_path) = @_;
 my $pathname = "$backup_path/$relative_path";
 open(my $fh, '>', $pathname) || die "open $pathname: $!";
 print $fh "This is an extra file.\n";
 close($fh);
 return;
}

# Add a file into the root directory of the backup.
sub mutilate_extra_file
{
 my ($backup_path) = @_;
 create_extra_file($backup_path, "extra_file");
 return;
}

# Add a file inside the user-defined tablespace.
sub mutilate_extra_tablespace_file
{
 my ($backup_path) = @_;
 my ($tsoid) =
   grep { $_ ne '.' && $_ ne '..' } slurp_dir("$backup_path/pg_tblspc");
 my ($catvdir) = grep { $_ ne '.' && $_ ne '..' }
   slurp_dir("$backup_path/pg_tblspc/$tsoid");
 my ($tsdboid) = grep { $_ ne '.' && $_ ne '..' }
   slurp_dir("$backup_path/pg_tblspc/$tsoid/$catvdir");
 create_extra_file($backup_path,
  "pg_tblspc/$tsoid/$catvdir/$tsdboid/extra_ts_file");
 return;
}

# Remove a file.
sub mutilate_missing_file
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/pg_xact/0000";
 unlink($pathname) || die "$pathname: $!";
 return;
}

# Remove the symlink to the user-defined tablespace.
sub mutilate_missing_tablespace
{
 my ($backup_path) = @_;
 my ($tsoid) =
   grep { $_ ne '.' && $_ ne '..' } slurp_dir("$backup_path/pg_tblspc");
 my $pathname = "$backup_path/pg_tblspc/$tsoid";
 if ($windows_os)
 {
  # rmdir works on some windows setups, unlink on others.
  # Instead of trying to implement precise rules, just try one and then
  # the other.
  unless (rmdir($pathname))
  {
   my $err = $!;
   unlink($pathname) || die "$pathname: rmdir: $err, unlink: $!";
  }
 }
 else
 {
  unlink($pathname) || die "$pathname: $!";
 }
 return;
}

# Append an additional bytes to a file.
sub mutilate_append_to_file
{
 my ($backup_path) = @_;
 append_to_file "$backup_path/global/pg_control"'x';
 return;
}

# Truncate a file to zero length.
sub mutilate_truncate_file
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/pg_hba.conf";
 open(my $fh, '>', $pathname) || die "open $pathname: $!";
 close($fh);
 return;
}

# Replace a file's contents without changing the length of the file. This is
# not a particularly efficient way to do this, so we pick a file that's
# expected to be short.
sub mutilate_replace_file
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/PG_VERSION";
 my $contents = slurp_file($pathname);
 open(my $fh, '>', $pathname) || die "open $pathname: $!";
 print $fh 'q' x length($contents);
 close($fh);
 return;
}

# Copy manifest of other backups to demonstrate the case where the wrong
# manifest is referred
sub mutilate_system_identifier
{
 my ($backup_path) = @_;

 # Set up another new database instance with different system identifier and
 # make backup
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init(force_initdb => 1, allows_streaming => 1);
 $node->start;
 $node->backup('backup2');
 move($node->backup_dir . '/backup2/backup_manifest',
  $backup_path . '/backup_manifest')
   or BAIL_OUT "could not copy manifest to $backup_path";
 $node->teardown_node(fail_ok => 1);
 return;
}

# Corrupt the backup manifest.
sub mutilate_bad_manifest
{
 my ($backup_path) = @_;
 append_to_file "$backup_path/backup_manifest""\n";
 return;
}

# Create a file that can't be opened. (This is skipped on Windows.)
sub mutilate_open_file_fails
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/PG_VERSION";
 chmod(0, $pathname) || die "chmod $pathname: $!";
 return;
}

# Create a directory that can't be opened. (This is skipped on Windows.)
sub mutilate_open_directory_fails
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/pg_subtrans";
 chmod(0, $pathname) || die "chmod $pathname: $!";
 return;
}

# restore permissions on the unreadable directory we created.
sub cleanup_open_directory_fails
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/pg_subtrans";
 chmod(0700, $pathname) || die "chmod $pathname: $!";
 return;
}

# Create a directory that can't be searched. (This is skipped on Windows.)
sub mutilate_search_directory_fails
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/base";
 chmod(0400, $pathname) || die "chmod $pathname: $!";
 return;
}

# rmtree can't cope with a mode 400 directory, so change back to 700.
sub cleanup_search_directory_fails
{
 my ($backup_path) = @_;
 my $pathname = "$backup_path/base";
 chmod(0700, $pathname) || die "chmod $pathname: $!";
 return;
}

done_testing();

Messung V0.5 in Prozent
C=90 H=93 G=91

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002