diff options
author | Charles-François Natali <neologix@free.fr> | 2011-10-31 19:47:31 (GMT) |
---|---|---|
committer | Charles-François Natali <neologix@free.fr> | 2011-10-31 19:47:31 (GMT) |
commit | e695eec24addd2a39c9af7456b8218c0810d742c (patch) | |
tree | f6e5c0cbb2aaa4d6a3ddbec9425fda2e4d885c12 | |
parent | 59142db6d35f00142cd9982878e75d43cbda7a68 (diff) | |
download | cpython-e695eec24addd2a39c9af7456b8218c0810d742c.zip cpython-e695eec24addd2a39c9af7456b8218c0810d742c.tar.gz cpython-e695eec24addd2a39c9af7456b8218c0810d742c.tar.bz2 |
Issue #13303: Fix a race condition in the bytecode file creation.
-rw-r--r-- | Lib/importlib/_bootstrap.py | 7 | ||||
-rw-r--r-- | Python/import.c | 50 |
2 files changed, 12 insertions, 45 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 4161b3e..775fa85 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -85,10 +85,11 @@ def _write_atomic(path, data): Be prepared to handle a FileExistsError if concurrent writing of the temporary file is attempted.""" if not sys.platform.startswith('win'): - # On POSIX-like platforms, renaming is atomic - path_tmp = path + '.tmp' + # On POSIX-like platforms, renaming is atomic. id() is used to generate + # a pseudo-random filename. + path_tmp = '{}.{}'.format(path, id(path)) + fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY) try: - fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY) with _io.FileIO(fd, 'wb') as file: file.write(data) _os.rename(path_tmp, path) diff --git a/Python/import.c b/Python/import.c index 03e9986..701041b 100644 --- a/Python/import.c +++ b/Python/import.c @@ -1183,42 +1183,6 @@ parse_source_module(PyObject *pathname, FILE *fp) return co; } -/* Helper to open a bytecode file for writing in exclusive mode */ - -#ifndef MS_WINDOWS -static FILE * -open_exclusive(char *filename, mode_t mode) -{ -#if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC) - /* Use O_EXCL to avoid a race condition when another process tries to - write the same file. When that happens, our open() call fails, - which is just fine (since it's only a cache). - XXX If the file exists and is writable but the directory is not - writable, the file will never be written. Oh well. - */ - int fd; - (void) unlink(filename); - fd = open(filename, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC -#ifdef O_BINARY - |O_BINARY /* necessary for Windows */ -#endif -#ifdef __VMS - , mode, "ctxt=bin", "shr=nil" -#else - , mode -#endif - ); - if (fd < 0) - return NULL; - return fdopen(fd, "wb"); -#else - /* Best we can do -- on Windows this can't happen anyway */ - return fopen(filename, "wb"); -#endif -} -#endif - - /* 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 @@ -1234,15 +1198,13 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname, #ifdef MS_WINDOWS /* since Windows uses different permissions */ mode_t mode = srcstat->st_mode & ~S_IEXEC; #else - mode_t mode = srcstat->st_mode & ~S_IXUSR & ~S_IXGRP & ~S_IXOTH; mode_t dirmode = (srcstat->st_mode | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR | S_IWGRP | S_IWOTH); PyObject *dirbytes; #endif -#ifdef MS_WINDOWS int fd; -#else +#ifndef MS_WINDOWS PyObject *cpathbytes, *cpathbytes_tmp; Py_ssize_t cpathbytes_len; #endif @@ -1313,7 +1275,7 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname, return; } cpathbytes_len = PyBytes_GET_SIZE(cpathbytes); - cpathbytes_tmp = PyBytes_FromStringAndSize(NULL, cpathbytes_len + 4); + cpathbytes_tmp = PyBytes_FromStringAndSize(NULL, cpathbytes_len + 6); if (cpathbytes_tmp == NULL) { Py_DECREF(cpathbytes); PyErr_Clear(); @@ -1321,9 +1283,13 @@ write_compiled_module(PyCodeObject *co, PyObject *cpathname, } memcpy(PyBytes_AS_STRING(cpathbytes_tmp), PyBytes_AS_STRING(cpathbytes), cpathbytes_len); - memcpy(PyBytes_AS_STRING(cpathbytes_tmp) + cpathbytes_len, ".tmp", 4); + memcpy(PyBytes_AS_STRING(cpathbytes_tmp) + cpathbytes_len, "XXXXXX", 6); - fp = open_exclusive(PyBytes_AS_STRING(cpathbytes_tmp), mode); + fd = mkstemp(PyBytes_AS_STRING(cpathbytes_tmp)); + if (0 <= fd) + fp = fdopen(fd, "wb"); + else + fp = NULL; #endif if (fp == NULL) { if (Py_VerboseFlag) |