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);
 }