components/python/python26/patches/Python26-06-write_compiled_module-atomic.patch
author Rich Burridge <rich.burridge@oracle.com>
Wed, 30 May 2012 12:40:14 -0700
changeset 841 1a62cefa636d
parent 115 c360825c3a3f
permissions -rw-r--r--
7121707 python 2.6.4 should be updated to latest 2.6.X release

diff --git Python-2.6.4/Python/import.c Python-2.6.4/Python/import.c
--- Python-2.6.4/Python/import.c
+++ Python-2.6.4/Python/import.c
@@ -868,8 +868,9 @@
 
 /* Write a compiled module to a file, placing the time of last
    modification of its source into the header.
-   Errors are ignored, if a write error occurs an attempt is made to
-   remove the file. */
+   Write to a temporary file first so that creating the file is atomic.
+   Errors are ignored, if a write/unlink/rename error occurs an attempt
+   is made to remove the temporary file. */
 
 static void
 write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat)
@@ -882,11 +883,21 @@
     mode_t mode = srcstat->st_mode & ~S_IXUSR & ~S_IXGRP & ~S_IXOTH;
 #endif
 
-    fp = open_exclusive(cpathname, mode);
+    char *tmppathname;
+
+    /* the temporary file is called cpathname + ".tmp" */
+    if ((tmppathname = PyMem_Malloc(strlen(cpathname) + strlen(".tmp") + 1))
+        == NULL) {
+        return;
+    }
+    sprintf (tmppathname, "%s.tmp", cpathname);
+    fp = open_exclusive(tmppathname, mode);
+
     if (fp == NULL) {
         if (Py_VerboseFlag)
             PySys_WriteStderr(
-                "# can't create %s\n", cpathname);
+                "# can't create %s\n", tmppathname);
+        PyMem_Free(tmppathname);
         return;
     }
     PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION);
@@ -895,10 +906,11 @@
     PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
     if (fflush(fp) != 0 || ferror(fp)) {
         if (Py_VerboseFlag)
-            PySys_WriteStderr("# can't write %s\n", cpathname);
+            PySys_WriteStderr("# can't write %s\n", tmppathname);
         /* Don't keep partial file */
         fclose(fp);
-        (void) unlink(cpathname);
+        (void) unlink(tmppathname);
+        PyMem_Free(tmppathname);
         return;
     }
     /* Now write the true mtime */
@@ -907,8 +919,29 @@
     PyMarshal_WriteLongToFile((long)mtime, fp, Py_MARSHAL_VERSION);
     fflush(fp);
     fclose(fp);
+    /* Delete the old compiled file, if exists */
+    if (unlink (cpathname)) {
+        if ((errno != ENOENT)) {
+            /* the file exists but could not be deleted */
+            if (Py_VerboseFlag)
+                PySys_WriteStderr("# can't unlink %s\n", cpathname);
+            (void) unlink(tmppathname);
+            PyMem_Free(tmppathname);
+            return;
+        }
+    }
+    /* rename the tmp file to the real file name */
+    if (rename (tmppathname, cpathname)) {
+        if (Py_VerboseFlag)
+            PySys_WriteStderr(
+                "# can't rename %s to %s\n", tmppathname, cpathname);
+        (void) unlink(tmppathname);
+        PyMem_Free(tmppathname);
+        return;
+    }
     if (Py_VerboseFlag)
         PySys_WriteStderr("# wrote %s\n", cpathname);
+    PyMem_Free(tmppathname);
 }
 
 static void