Fix low-probability bug in relcache startup: write_irels wrote the
authorTom Lane <[email protected]>
Fri, 31 Mar 2000 19:39:22 +0000 (19:39 +0000)
committerTom Lane <[email protected]>
Fri, 31 Mar 2000 19:39:22 +0000 (19:39 +0000)
pg_internal.init file in-place, which meant that if another backend
started at about the same time, it might read the incomplete file.
init_irels tries to guard against that, but I have now seen a crash
due to reading bad data from a partly-written file.  (This may indicate
a kernel bug on my platform?  Not sure.)  Anyway, clearly the safest
course is to write the new pg_internal.init file under a unique temporary
filename, and rename it into place only after it's all written.

src/backend/utils/cache/relcache.c

index 6ed3b7414ed1e3ed412c2bd37c390cca8086bf1c..97ec7d300d1fdbde1decf3229ee89090aeb3463e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.93 2000/03/17 02:36:27 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.94 2000/03/31 19:39:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,7 @@
 #include <errno.h>
 #include <sys/file.h>
 #include <fcntl.h>
+#include <unistd.h>
 
 #include "postgres.h"
 
@@ -2266,14 +2267,26 @@ write_irels(void)
    int         i;
    int         relno;
    RelationBuildDescInfo bi;
+   char        tempfilename[MAXPGPATH];
+   char        finalfilename[MAXPGPATH];
+
+   /*
+    * We must write a temporary file and rename it into place.  Otherwise,
+    * another backend starting at about the same time might crash trying to
+    * read the partially-complete file.
+    */
+   snprintf(tempfilename, sizeof(tempfilename), "%s%c%s.%d",
+            DatabasePath, SEP_CHAR, RELCACHE_INIT_FILENAME, MyProcPid);
+   snprintf(finalfilename, sizeof(finalfilename), "%s%c%s",
+            DatabasePath, SEP_CHAR, RELCACHE_INIT_FILENAME);
 
 #ifndef __CYGWIN32__
-   fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+   fd = PathNameOpenFile(tempfilename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
 #else
-   fd = FileNameOpenFile(RELCACHE_INIT_FILENAME, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
+   fd = PathNameOpenFile(tempfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
 #endif
    if (fd < 0)
-       elog(FATAL, "cannot create init file %s", RELCACHE_INIT_FILENAME);
+       elog(FATAL, "cannot create init file %s", tempfilename);
 
    FileSeek(fd, 0L, SEEK_SET);
 
@@ -2397,4 +2410,10 @@ write_irels(void)
    }
 
    FileClose(fd);
+
+    /*
+     * And rename the temp file to its final name, deleting any previously-
+    * existing init file.
+     */
+    rename(tempfilename, finalfilename);
 }