rewind-readonly-fix-v1.patch
text/x-diff
Filename: rewind-readonly-fix-v1.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch v1
| File | + | − |
|---|---|---|
| src/bin/pg_rewind/copy_fetch.c | 15 | 6 |
| src/bin/pg_rewind/filemap.c | 10 | 5 |
| src/bin/pg_rewind/filemap.h | 4 | 1 |
| src/bin/pg_rewind/file_ops.c | 20 | 5 |
| src/bin/pg_rewind/file_ops.h | 2 | 1 |
| src/bin/pg_rewind/libpq_fetch.c | 26 | 4 |
| src/bin/pg_rewind/pg_rewind.c | 3 | 2 |
| src/bin/pg_rewind/t/006_readonly.pl | 46 | 0 |
diff --git a/src/bin/pg_rewind/copy_fetch.c b/src/bin/pg_rewind/copy_fetch.c
index 04db409675..2696972421 100644
--- a/src/bin/pg_rewind/copy_fetch.c
+++ b/src/bin/pg_rewind/copy_fetch.c
@@ -154,9 +154,11 @@ recurse_dir(const char *datadir, const char *parentpath,
* Copy a file from source to target, between 'begin' and 'end' offsets.
*
* If 'trunc' is true, any existing file with the same name is truncated.
+ * If 'error_ok' is true, any failures when opening the file is ignored.
*/
static void
-rewind_copy_file_range(const char *path, off_t begin, off_t end, bool trunc)
+rewind_copy_file_range(const char *path, off_t begin, off_t end,
+ bool trunc, bool error_ok)
{
char buf[BLCKSZ];
char srcpath[MAXPGPATH];
@@ -172,7 +174,8 @@ rewind_copy_file_range(const char *path, off_t begin, off_t end, bool trunc)
if (lseek(srcfd, begin, SEEK_SET) == -1)
pg_fatal("could not seek in source file: %s\n", strerror(errno));
- open_target_file(path, trunc);
+ if (!open_target_file(path, trunc, error_ok ? PG_WARNING : PG_FATAL))
+ return;
while (end - begin > 0)
{
@@ -221,8 +224,14 @@ copy_executeFileMap(filemap_t *map)
/* ok, do nothing.. */
break;
- case FILE_ACTION_COPY:
- rewind_copy_file_range(entry->path, 0, entry->newsize, true);
+ case FILE_ACTION_COPY_CONFIG:
+ rewind_copy_file_range(entry->path, 0, entry->newsize, true,
+ true);
+ break;
+
+ case FILE_ACTION_COPY_DATA:
+ rewind_copy_file_range(entry->path, 0, entry->newsize, true,
+ false);
break;
case FILE_ACTION_TRUNCATE:
@@ -231,7 +240,7 @@ copy_executeFileMap(filemap_t *map)
case FILE_ACTION_COPY_TAIL:
rewind_copy_file_range(entry->path, entry->oldsize,
- entry->newsize, false);
+ entry->newsize, false, false);
break;
case FILE_ACTION_CREATE:
@@ -258,7 +267,7 @@ execute_pagemap(datapagemap_t *pagemap, const char *path)
while (datapagemap_next(iter, &blkno))
{
offset = blkno * BLCKSZ;
- rewind_copy_file_range(path, offset, offset + BLCKSZ, false);
+ rewind_copy_file_range(path, offset, offset + BLCKSZ, false, false);
/* Ok, this block has now been copied from new data dir to old */
}
pg_free(iter);
diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c
index 705383d184..290a4a1c5b 100644
--- a/src/bin/pg_rewind/file_ops.c
+++ b/src/bin/pg_rewind/file_ops.c
@@ -37,19 +37,21 @@ static void remove_target_symlink(const char *path);
/*
* Open a target file for writing. If 'trunc' is true and the file already
- * exists, it will be truncated.
+ * exists, it will be truncated. 'elevel' can be used to mark as non-fatal
+ * errors happening on non-readable files. Returns true if the file has been
+ * successfully opened, and false otherwise.
*/
-void
-open_target_file(const char *path, bool trunc)
+bool
+open_target_file(const char *path, bool trunc, int elevel)
{
int mode;
if (dry_run)
- return;
+ return true;
if (dstfd != -1 && !trunc &&
strcmp(path, &dstpath[strlen(datadir_target) + 1]) == 0)
- return; /* already open */
+ return true; /* already open */
close_target_file();
@@ -59,9 +61,22 @@ open_target_file(const char *path, bool trunc)
if (trunc)
mode |= O_TRUNC;
dstfd = open(dstpath, mode, 0600);
+
+ if (errno == EACCES)
+ {
+ /* Ignore errors trying to open unreadable files */
+ pg_log(elevel, "could not open target file \"%s\": %s\n",
+ dstpath, strerror(errno));
+
+ /* Reset stack for successive callers */
+ errno = 0;
+ return false;
+ }
+
if (dstfd < 0)
pg_fatal("could not open target file \"%s\": %s\n",
dstpath, strerror(errno));
+ return true;
}
/*
diff --git a/src/bin/pg_rewind/file_ops.h b/src/bin/pg_rewind/file_ops.h
index be580ee4db..17d0952e5f 100644
--- a/src/bin/pg_rewind/file_ops.h
+++ b/src/bin/pg_rewind/file_ops.h
@@ -12,7 +12,8 @@
#include "filemap.h"
-extern void open_target_file(const char *path, bool trunc);
+extern bool open_target_file(const char *path, bool trunc, int elevel);
+extern bool is_target_file(const char *path);
extern void write_target_range(char *buf, off_t begin, size_t size);
extern void close_target_file(void);
extern void truncate_target_file(const char *path, off_t newsize);
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
index 19da1ee7e0..fe273864b2 100644
--- a/src/bin/pg_rewind/filemap.c
+++ b/src/bin/pg_rewind/filemap.c
@@ -176,7 +176,8 @@ process_source_file(const char *path, file_type_t type, size_t newsize,
}
else
{
- action = FILE_ACTION_COPY;
+ action = isRelDataFile(path) ? FILE_ACTION_COPY_DATA :
+ FILE_ACTION_COPY_CONFIG;
oldsize = 0;
}
}
@@ -392,7 +393,8 @@ process_block_change(ForkNumber forknum, RelFileNode rnode, BlockNumber blkno)
datapagemap_add(&entry->pagemap, blkno_inseg);
break;
- case FILE_ACTION_COPY:
+ case FILE_ACTION_COPY_DATA:
+ case FILE_ACTION_COPY_CONFIG:
case FILE_ACTION_REMOVE:
break;
@@ -456,8 +458,10 @@ action_to_str(file_action_t action)
{
case FILE_ACTION_NONE:
return "NONE";
- case FILE_ACTION_COPY:
- return "COPY";
+ case FILE_ACTION_COPY_DATA:
+ return "COPY_DATA";
+ case FILE_ACTION_COPY_CONFIG:
+ return "COPY_CONFIG";
case FILE_ACTION_TRUNCATE:
return "TRUNCATE";
case FILE_ACTION_COPY_TAIL:
@@ -494,7 +498,8 @@ calculate_totals(void)
map->total_size += entry->newsize;
- if (entry->action == FILE_ACTION_COPY)
+ if (entry->action == FILE_ACTION_COPY_CONFIG ||
+ entry->action == FILE_ACTION_COPY_DATA)
{
map->fetch_size += entry->newsize;
continue;
diff --git a/src/bin/pg_rewind/filemap.h b/src/bin/pg_rewind/filemap.h
index 63ba32a6cd..28e48762ef 100644
--- a/src/bin/pg_rewind/filemap.h
+++ b/src/bin/pg_rewind/filemap.h
@@ -24,7 +24,10 @@
typedef enum
{
FILE_ACTION_CREATE, /* create local directory or symbolic link */
- FILE_ACTION_COPY, /* copy whole file, overwriting if exists */
+ FILE_ACTION_COPY_CONFIG, /* copy whole configuration file, overwriting
+ * if exists */
+ FILE_ACTION_COPY_DATA, /* copy whole relation file, overwriting if
+ * exists */
FILE_ACTION_COPY_TAIL, /* copy tail from 'oldsize' to 'newsize' */
FILE_ACTION_NONE, /* no action (we might still copy modified
* blocks based on the parsed WAL) */
diff --git a/src/bin/pg_rewind/libpq_fetch.c b/src/bin/pg_rewind/libpq_fetch.c
index d1726d1c74..c015603a93 100644
--- a/src/bin/pg_rewind/libpq_fetch.c
+++ b/src/bin/pg_rewind/libpq_fetch.c
@@ -326,7 +326,11 @@ receiveFileChunks(const char *sql)
pg_log(PG_DEBUG, "received chunk for file \"%s\", offset %s, size %d\n",
filename, chunkoff_str, chunksize);
- open_target_file(filename, false);
+ /*
+ * File chunks are present for data files, so consider any failures
+ * as fatal.
+ */
+ open_target_file(filename, false, PG_FATAL);
write_target_range(chunk, chunkoff, chunksize);
@@ -452,9 +456,27 @@ libpq_executeFileMap(filemap_t *map)
/* nothing else to do */
break;
- case FILE_ACTION_COPY:
- /* Truncate the old file out of the way, if any */
- open_target_file(entry->path, true);
+ case FILE_ACTION_COPY_CONFIG:
+ /*
+ * Truncate the old file out of the way, if any. Any
+ * failure in opening the file is not critical here. We
+ * are dealing with a configuration files here, so
+ * ignore any failures if the file is unreadable in the
+ * target.
+ */
+ if (!open_target_file(entry->path, true, PG_WARNING))
+ break;
+ fetch_file_range(entry->path, 0, entry->newsize);
+ break;
+
+ case FILE_ACTION_COPY_DATA:
+ /*
+ * Truncate the old file out of the way, if any. We
+ * are dealing with a relation file which normally
+ * exists on the source but not on the target, so
+ * consider any failures as fatal.
+ */
+ open_target_file(entry->path, true, PG_FATAL);
fetch_file_range(entry->path, 0, entry->newsize);
break;
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index 72ab2f8d5e..0997062d86 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -597,7 +597,8 @@ createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpo
pg_fatal("backup label buffer too small\n"); /* shouldn't happen */
/* TODO: move old file out of the way, if any. */
- open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
+ open_target_file("backup_label", true, PG_FATAL);
+
write_target_range(buf, 0, len);
close_target_file();
}
@@ -675,7 +676,7 @@ updateControlFile(ControlFileData *ControlFile)
memset(buffer, 0, PG_CONTROL_FILE_SIZE);
memcpy(buffer, ControlFile, sizeof(ControlFileData));
- open_target_file("global/pg_control", false);
+ open_target_file("global/pg_control", false, PG_FATAL);
write_target_range(buffer, 0, PG_CONTROL_FILE_SIZE);
diff --git a/src/bin/pg_rewind/t/006_readonly.pl b/src/bin/pg_rewind/t/006_readonly.pl
new file mode 100644
index 0000000000..5807110dad
--- /dev/null
+++ b/src/bin/pg_rewind/t/006_readonly.pl
@@ -0,0 +1,46 @@
+# Test how pg_rewind reacts to read-only files in the data dirs.
+# All such files should be ignored in the process.
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 2;
+
+use File::Find;
+
+use RewindTest;
+
+
+sub run_test
+{
+ my $test_mode = shift;
+
+ RewindTest::setup_cluster($test_mode);
+ RewindTest::start_master();
+ RewindTest::create_standby($test_mode);
+
+ # Create the same read-only file in standby and master
+ my $test_master_datadir = $node_master->data_dir;
+ my $test_standby_datadir = $node_standby->data_dir;
+ my $first_readonly_master = "$test_master_datadir/aaa_readonly_file";
+ my $first_readonly_standby = "$test_standby_datadir/aaa_readonly_file";
+ my $last_readonly_master = "$test_master_datadir/zzz_readonly_file";
+ my $last_readonly_standby = "$test_standby_datadir/zzz_readonly_file";
+
+ append_to_file($first_readonly_master, "in master");
+ append_to_file($first_readonly_standby, "in standby");
+ append_to_file($last_readonly_master, "in master");
+ append_to_file($last_readonly_standby, "in standby");
+ #chmod 0400, $first_readonly_master, $first_readonly_standby;
+ chmod 0400, $last_readonly_master, $last_readonly_standby;
+
+ RewindTest::promote_standby();
+ RewindTest::run_pg_rewind($test_mode);
+ RewindTest::clean_rewind_test();
+}
+
+# Run the test in both modes.
+run_test('local');
+run_test('remote');
+
+exit(0);