Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bb87edc

Browse files
dwsteeleCommitfest Bot
authored and
Commitfest Bot
committed
Add pg_control flag to prevent recovery without backup_label.
Harden recovery by adding a flag to pg_control to indicate that backup_label is required. This prevents the user from deleting backup_label resulting in an inconsistent recovery. Another advantage is that the copy of pg_control used by pg_basebackup is guaranteed not to be torn. This functionality is limited to pg_basebackup (or any software comfortable with modifying pg_control). Control and catalog version bumps are required.
1 parent 05883bd commit bb87edc

File tree

12 files changed

+80
-15
lines changed

12 files changed

+80
-15
lines changed

doc/src/sgml/func.sgml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28256,6 +28256,11 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
2825628256
<entry><type>boolean</type></entry>
2825728257
</row>
2825828258

28259+
<row>
28260+
<entry><structfield>backup_label_required</structfield></entry>
28261+
<entry><type>boolean</type></entry>
28262+
</row>
28263+
2825928264
</tbody>
2826028265
</tgroup>
2826128266
</table>

src/backend/access/transam/xlog.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9590,6 +9590,29 @@ do_pg_abort_backup(int code, Datum arg)
95909590
}
95919591
}
95929592

9593+
/*
9594+
* Create a consistent copy of control data to be used for backup and update it
9595+
* to require a backup label for recovery. Also recalculate the CRC.
9596+
*/
9597+
void
9598+
backup_control_file(uint8_t *controlFile)
9599+
{
9600+
ControlFileData *controlData = ((ControlFileData *)controlFile);
9601+
9602+
memset(controlFile + sizeof(ControlFileData), 0,
9603+
PG_CONTROL_FILE_SIZE - sizeof(ControlFileData));
9604+
9605+
LWLockAcquire(ControlFileLock, LW_SHARED);
9606+
memcpy(controlFile, ControlFile, sizeof(ControlFileData));
9607+
LWLockRelease(ControlFileLock);
9608+
9609+
controlData->backupLabelRequired = true;
9610+
9611+
INIT_CRC32C(controlData->crc);
9612+
COMP_CRC32C(controlData->crc, controlFile, offsetof(ControlFileData, crc));
9613+
FIN_CRC32C(controlData->crc);
9614+
}
9615+
95939616
/*
95949617
* Register a handler that will warn about unterminated backups at end of
95959618
* session, unless this has already been done.

src/backend/access/transam/xlogrecovery.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,14 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
709709
}
710710
else
711711
{
712-
/* No backup_label file has been found if we are here. */
712+
/*
713+
* No backup_label file has been found if we are here. Error if the
714+
* control file requires backup_label.
715+
*/
716+
if (ControlFile->backupLabelRequired)
717+
ereport(FATAL,
718+
(errmsg("could not find backup_label required for recovery"),
719+
errhint("backup_label must be present for recovery to proceed")));
713720

714721
/*
715722
* If tablespace_map file is present without backup_label file, there
@@ -984,6 +991,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
984991
{
985992
ControlFile->backupStartPoint = checkPoint.redo;
986993
ControlFile->backupEndRequired = backupEndRequired;
994+
ControlFile->backupLabelRequired = false;
987995

988996
if (backupFromStandby)
989997
{

src/backend/backup/basebackup.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "backup/basebackup_incremental.h"
2424
#include "backup/basebackup_sink.h"
2525
#include "backup/basebackup_target.h"
26+
#include "catalog/pg_control.h"
2627
#include "catalog/pg_tablespace_d.h"
2728
#include "commands/defrem.h"
2829
#include "common/compression.h"
@@ -326,9 +327,9 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
326327

327328
if (ti->path == NULL)
328329
{
329-
struct stat statbuf;
330330
bool sendtblspclinks = true;
331331
char *backup_label;
332+
uint8_t controlFile[PG_CONTROL_FILE_SIZE];
332333

333334
bbsink_begin_archive(sink, "base.tar");
334335

@@ -351,14 +352,10 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
351352
sendtblspclinks, &manifest, InvalidOid, ib);
352353

353354
/* ... and pg_control after everything else. */
354-
if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
355-
ereport(ERROR,
356-
(errcode_for_file_access(),
357-
errmsg("could not stat file \"%s\": %m",
358-
XLOG_CONTROL_FILE)));
359-
sendFile(sink, XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf,
360-
false, InvalidOid, InvalidOid,
361-
InvalidRelFileNumber, 0, &manifest, 0, NULL, 0);
355+
backup_control_file(controlFile);
356+
sendFileWithContent(sink, XLOG_CONTROL_FILE,
357+
(char *)controlFile, PG_CONTROL_FILE_SIZE,
358+
&manifest);
362359
}
363360
else
364361
{

src/backend/utils/misc/pg_controldata.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
162162
Datum
163163
pg_control_recovery(PG_FUNCTION_ARGS)
164164
{
165-
Datum values[5];
166-
bool nulls[5];
165+
Datum values[6];
166+
bool nulls[6];
167167
TupleDesc tupdesc;
168168
HeapTuple htup;
169169
ControlFileData *ControlFile;
@@ -195,6 +195,9 @@ pg_control_recovery(PG_FUNCTION_ARGS)
195195
values[4] = BoolGetDatum(ControlFile->backupEndRequired);
196196
nulls[4] = false;
197197

198+
values[5] = BoolGetDatum(ControlFile->backupLabelRequired);
199+
nulls[5] = false;
200+
198201
htup = heap_form_tuple(tupdesc, values, nulls);
199202

200203
PG_RETURN_DATUM(HeapTupleGetDatum(htup));

src/bin/pg_controldata/pg_controldata.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ main(int argc, char *argv[])
294294
LSN_FORMAT_ARGS(ControlFile->backupEndPoint));
295295
printf(_("End-of-backup record required: %s\n"),
296296
ControlFile->backupEndRequired ? _("yes") : _("no"));
297+
printf(_("Backup label required: %s\n"),
298+
ControlFile->backupLabelRequired ? _("yes") : _("no"));
297299
printf(_("wal_level setting: %s\n"),
298300
wal_level_str(ControlFile->wal_level));
299301
printf(_("wal_log_hints setting: %s\n"),

src/bin/pg_resetwal/pg_resetwal.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ RewriteControlFile(void)
899899
ControlFile.backupStartPoint = 0;
900900
ControlFile.backupEndPoint = 0;
901901
ControlFile.backupEndRequired = false;
902+
ControlFile.backupLabelRequired = false;
902903

903904
/*
904905
* Force the defaults for max_* settings. The values don't really matter

src/bin/pg_rewind/pg_rewind.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
727727
ControlFile_new.minRecoveryPoint = endrec;
728728
ControlFile_new.minRecoveryPointTLI = endtli;
729729
ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
730+
ControlFile_new.backupLabelRequired = true;
730731
if (!dry_run)
731732
update_controlfile(datadir_target, &ControlFile_new, do_sync);
732733
}

src/include/access/xlog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ extern void do_pg_backup_start(const char *backupidstr, bool fast,
296296
StringInfo tblspcmapfile);
297297
extern void do_pg_backup_stop(BackupState *state, bool waitforarchive);
298298
extern void do_pg_abort_backup(int code, Datum arg);
299+
extern void backup_control_file(uint8_t *controlFile);
299300
extern void register_persistent_abort_backup_handler(void);
300301
extern SessionBackupState get_backup_status(void);
301302

src/include/catalog/pg_control.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,16 @@ typedef struct ControlFileData
164164
* If backupEndRequired is true, we know for sure that we're restoring
165165
* from a backup, and must see a backup-end record before we can safely
166166
* start up.
167+
*
168+
* If backupLabelRequired is true, then a backup_label file must be
169+
* present in order for recovery to proceed.
167170
*/
168171
XLogRecPtr minRecoveryPoint;
169172
TimeLineID minRecoveryPointTLI;
170173
XLogRecPtr backupStartPoint;
171174
XLogRecPtr backupEndPoint;
172175
bool backupEndRequired;
176+
bool backupLabelRequired;
173177

174178
/*
175179
* Parameter settings that determine if the WAL can be used for archival

src/include/catalog/pg_proc.dat

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12304,9 +12304,9 @@
1230412304
{ oid => '3443',
1230512305
descr => 'pg_controldata recovery state information as a function',
1230612306
proname => 'pg_control_recovery', provolatile => 'v', prorettype => 'record',
12307-
proargtypes => '', proallargtypes => '{pg_lsn,int4,pg_lsn,pg_lsn,bool}',
12308-
proargmodes => '{o,o,o,o,o}',
12309-
proargnames => '{min_recovery_end_lsn,min_recovery_end_timeline,backup_start_lsn,backup_end_lsn,end_of_backup_record_required}',
12307+
proargtypes => '', proallargtypes => '{pg_lsn,int4,pg_lsn,pg_lsn,bool,bool}',
12308+
proargmodes => '{o,o,o,o,o,o}',
12309+
proargnames => '{min_recovery_end_lsn,min_recovery_end_timeline,backup_start_lsn,backup_end_lsn,end_of_backup_record_required,backup_label_required}',
1231012310
prosrc => 'pg_control_recovery' },
1231112311

1231212312
{ oid => '3444',

src/test/recovery/t/002_archiving.pl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@
4141
archive_cleanup_command = 'echo archive_cleanup_done > $archive_cleanup_command_file'
4242
recovery_end_command = 'echo recovery_ended_done > $recovery_end_command_file'
4343
));
44+
45+
# Rename backup_label to verify that recovery will not start without it
46+
rename("$data_dir/backup_label", "$data_dir/backup_label.tmp")
47+
or BAIL_OUT "could not move $data_dir/backup_label";
48+
49+
my $res = run_log(
50+
[
51+
'pg_ctl', '-D', $node_standby->data_dir, '-l',
52+
$node_standby->logfile, 'start'
53+
]);
54+
ok(!$res, 'invalid recovery startup fails');
55+
56+
my $logfile = slurp_file($node_standby->logfile());
57+
ok($logfile =~ qr/could not find backup_label required for recovery/,
58+
'could not find backup_label required for recovery');
59+
60+
# Restore backup_label so recovery proceeds normally
61+
rename("$data_dir/backup_label.tmp", "$data_dir/backup_label")
62+
or BAIL_OUT "could not move $data_dir/backup_label";
63+
4464
$node_standby->start;
4565

4666
# Create some content on primary

0 commit comments

Comments
 (0)