aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/file/fd.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2015-05-04 14:13:53 -0400
committerRobert Haas <rhaas@postgresql.org>2015-05-04 14:13:53 -0400
commit2ce439f3379aed857517c8ce207485655000fc8e (patch)
tree196ccf42f41939b101aa6d4587ca93c8060b51a2 /src/backend/storage/file/fd.c
parentec3d976bce7e322c29f1007d19b63b7a3a1a6ee4 (diff)
downloadpostgresql-2ce439f3379aed857517c8ce207485655000fc8e.tar.gz
postgresql-2ce439f3379aed857517c8ce207485655000fc8e.zip
Recursively fsync() the data directory after a crash.
Otherwise, if there's another crash, some writes from after the first crash might make it to disk while writes from before the crash fail to make it to disk. This could lead to data corruption. Back-patch to all supported versions. Abhijit Menon-Sen, reviewed by Andres Freund and slightly revised by me.
Diffstat (limited to 'src/backend/storage/file/fd.c')
-rw-r--r--src/backend/storage/file/fd.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index f7967178c69..99dac841ffa 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -2439,3 +2439,118 @@ looks_like_temp_rel_name(const char *name)
return false;
return true;
}
+
+/*
+ * Hint to the OS that it should get ready to fsync() this file.
+ *
+ * Adapted from pre_sync_fname in initdb.c
+ */
+void
+pre_sync_fname(char *fname, bool isdir)
+{
+ int fd;
+
+ fd = open(fname, O_RDONLY | PG_BINARY);
+
+ /*
+ * Some OSs don't allow us to open directories at all (Windows returns
+ * EACCES)
+ */
+ if (fd < 0 && isdir && (errno == EISDIR || errno == EACCES))
+ return;
+
+ if (fd < 0)
+ ereport(FATAL,
+ (errmsg("could not open file \"%s\" before fsync",
+ fname)));
+
+ pg_flush_data(fd, 0, 0);
+
+ close(fd);
+}
+
+/*
+ * walkdir: recursively walk a directory, applying the action to each
+ * regular file and directory (including the named directory itself)
+ * and following symbolic links.
+ *
+ * NB: There is another version of walkdir in initdb.c, but that version
+ * behaves differently with respect to symbolic links. Caveat emptor!
+ */
+void
+walkdir(char *path, void (*action) (char *fname, bool isdir))
+{
+ DIR *dir;
+ struct dirent *de;
+
+ dir = AllocateDir(path);
+ while ((de = ReadDir(dir, path)) != NULL)
+ {
+ char subpath[MAXPGPATH];
+ struct stat fst;
+
+ CHECK_FOR_INTERRUPTS();
+
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0)
+ continue;
+
+ snprintf(subpath, MAXPGPATH, "%s/%s", path, de->d_name);
+
+ if (lstat(subpath, &fst) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", subpath)));
+
+ if (S_ISREG(fst.st_mode))
+ (*action) (subpath, false);
+ else if (S_ISDIR(fst.st_mode))
+ walkdir(subpath, action);
+#ifndef WIN32
+ else if (S_ISLNK(fst.st_mode))
+#else
+ else if (pg_win32_is_junction(subpath))
+#endif
+ {
+#if defined(HAVE_READLINK) || defined(WIN32)
+ char linkpath[MAXPGPATH];
+ int len;
+ struct stat lst;
+
+ len = readlink(subpath, linkpath, sizeof(linkpath)-1);
+ if (len < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not read symbolic link \"%s\": %m",
+ subpath)));
+
+ if (len >= sizeof(linkpath)-1)
+ ereport(ERROR,
+ (errmsg("symbolic link \"%s\" target is too long",
+ subpath)));
+
+ linkpath[len] = '\0';
+
+ if (lstat(linkpath, &lst) == 0)
+ {
+ if (S_ISREG(lst.st_mode))
+ (*action) (linkpath, false);
+ else if (S_ISDIR(lst.st_mode))
+ walkdir(subpath, action);
+ }
+ else if (errno != ENOENT)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", linkpath)));
+#else
+ ereport(WARNING,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("this platform does not support symbolic links; ignoring \"%s\"",
+ subpath)));
+#endif
+ }
+ }
+ FreeDir(dir);
+
+ (*action) (path, true);
+}