aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/timeline.c18
-rw-r--r--src/backend/access/transam/xlog.c10
-rw-r--r--src/backend/storage/file/fd.c63
3 files changed, 81 insertions, 10 deletions
diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c
index c9344e5e8b4..be21968293c 100644
--- a/src/backend/access/transam/timeline.c
+++ b/src/backend/access/transam/timeline.c
@@ -439,11 +439,14 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
/*
* Now move the completed history file into place with its final name.
- * The target file should not exist.
*/
TLHistoryFilePath(path, newTLI);
- Assert(access(path, F_OK) != 0 && errno == ENOENT);
- durable_rename(tmppath, path, ERROR);
+
+ /*
+ * Perform the rename using link if available, paranoidly trying to avoid
+ * overwriting an existing file (there shouldn't be one).
+ */
+ durable_rename_excl(tmppath, path, ERROR);
/* The history file can be archived immediately. */
if (XLogArchivingActive())
@@ -514,11 +517,14 @@ writeTimeLineHistoryFile(TimeLineID tli, char *content, int size)
/*
* Now move the completed history file into place with its final name.
- * The target file should not exist.
*/
TLHistoryFilePath(path, tli);
- Assert(access(path, F_OK) != 0 && errno == ENOENT);
- durable_rename(tmppath, path, ERROR);
+
+ /*
+ * Perform the rename using link if available, paranoidly trying to avoid
+ * overwriting an existing file (there shouldn't be one).
+ */
+ durable_rename_excl(tmppath, path, ERROR);
}
/*
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 45c84e3959c..61cda56c6ff 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -3323,12 +3323,14 @@ InstallXLogFileSegment(XLogSegNo *segno, char *tmppath,
}
}
- /* The target file should not exist */
- Assert(access(path, F_OK) != 0 && errno == ENOENT);
- if (durable_rename(tmppath, path, LOG) != 0)
+ /*
+ * Perform the rename using link if available, paranoidly trying to avoid
+ * overwriting an existing file (there shouldn't be one).
+ */
+ if (durable_rename_excl(tmppath, path, LOG) != 0)
{
LWLockRelease(ControlFileLock);
- /* durable_rename already emitted log message */
+ /* durable_rename_excl already emitted log message */
return false;
}
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f904f60c086..24704b6a023 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -808,6 +808,69 @@ durable_unlink(const char *fname, int elevel)
}
/*
+ * durable_rename_excl -- rename a file in a durable manner.
+ *
+ * Similar to durable_rename(), except that this routine tries (but does not
+ * guarantee) not to overwrite the target file.
+ *
+ * Note that a crash in an unfortunate moment can leave you with two links to
+ * the target file.
+ *
+ * Log errors with the caller specified severity.
+ *
+ * On Windows, using a hard link followed by unlink() causes concurrency
+ * issues, while a simple rename() does not cause that, so be careful when
+ * changing the logic of this routine.
+ *
+ * Returns 0 if the operation succeeded, -1 otherwise. Note that errno is not
+ * valid upon return.
+ */
+int
+durable_rename_excl(const char *oldfile, const char *newfile, int elevel)
+{
+ /*
+ * Ensure that, if we crash directly after the rename/link, a file with
+ * valid contents is moved into place.
+ */
+ if (fsync_fname_ext(oldfile, false, false, elevel) != 0)
+ return -1;
+
+#ifdef HAVE_WORKING_LINK
+ if (link(oldfile, newfile) < 0)
+ {
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg("could not link file \"%s\" to \"%s\": %m",
+ oldfile, newfile)));
+ return -1;
+ }
+ unlink(oldfile);
+#else
+ if (rename(oldfile, newfile) < 0)
+ {
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg("could not rename file \"%s\" to \"%s\": %m",
+ oldfile, newfile)));
+ return -1;
+ }
+#endif
+
+ /*
+ * Make change persistent in case of an OS crash, both the new entry and
+ * its parent directory need to be flushed.
+ */
+ if (fsync_fname_ext(newfile, false, false, elevel) != 0)
+ return -1;
+
+ /* Same for parent directory */
+ if (fsync_parent_path(newfile, elevel) != 0)
+ return -1;
+
+ return 0;
+}
+
+/*
* InitFileAccess --- initialize this module during backend startup
*
* This is called during either normal or standalone backend start.