1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

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.
This commit is contained in:
Robert Haas
2015-05-04 12:06:53 -04:00
parent 7c2ccb494c
commit 2bc3397168
3 changed files with 158 additions and 0 deletions

View File

@ -2265,3 +2265,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);
}