summaryrefslogtreecommitdiff
path: root/Python
diff options
context:
space:
mode:
Diffstat (limited to 'Python')
-rw-r--r--Python/errors.c4
-rw-r--r--Python/fileutils.c341
-rw-r--r--Python/import.c2
-rw-r--r--Python/pythonrun.c2
-rw-r--r--Python/random.c4
5 files changed, 334 insertions, 19 deletions
diff --git a/Python/errors.c b/Python/errors.c
index 7985eab536..93b1120191 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -1042,7 +1042,7 @@ PyErr_ProgramText(const char *filename, int lineno)
FILE *fp;
if (filename == NULL || *filename == '\0' || lineno <= 0)
return NULL;
- fp = fopen(filename, "r" PY_STDIOTEXTMODE);
+ fp = _Py_fopen(filename, "r" PY_STDIOTEXTMODE);
return err_programtext(fp, lineno);
}
@@ -1052,7 +1052,7 @@ PyErr_ProgramTextObject(PyObject *filename, int lineno)
FILE *fp;
if (filename == NULL || lineno <= 0)
return NULL;
- fp = _Py_fopen(filename, "r" PY_STDIOTEXTMODE);
+ fp = _Py_fopen_obj(filename, "r" PY_STDIOTEXTMODE);
return err_programtext(fp, lineno);
}
diff --git a/Python/fileutils.c b/Python/fileutils.c
index 698385504c..1cc74805cb 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -9,10 +9,29 @@
#include <langinfo.h>
#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+
#ifdef __APPLE__
extern wchar_t* _Py_DecodeUTF8_surrogateescape(const char *s, Py_ssize_t size);
#endif
+#ifdef O_CLOEXEC
+/* Does open() supports the O_CLOEXEC flag? Possible values:
+
+ -1: unknown
+ 0: open() ignores O_CLOEXEC flag, ex: Linux kernel older than 2.6.23
+ 1: open() supports O_CLOEXEC flag, close-on-exec is set
+
+ The flag is used by _Py_open(), io.FileIO and os.open() */
+int _Py_open_cloexec_works = -1;
+#endif
+
PyObject *
_Py_device_encoding(int fd)
{
@@ -547,14 +566,215 @@ _Py_stat(PyObject *path, struct stat *statbuf)
#endif
-/* Open a file. Use _wfopen() on Windows, encode the path to the locale
- encoding and use fopen() otherwise. */
+int
+get_inheritable(int fd, int raise)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ DWORD flags;
+
+ if (!_PyVerify_fd(fd)) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ if (!GetHandleInformation(handle, &flags)) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ return (flags & HANDLE_FLAG_INHERIT);
+#else
+ int flags;
+
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags == -1) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return !(flags & FD_CLOEXEC);
+#endif
+}
+
+/* Get the inheritable flag of the specified file descriptor.
+ Return 1 if it the file descriptor can be inherited, 0 if it cannot,
+ raise an exception and return -1 on error. */
+int
+_Py_get_inheritable(int fd)
+{
+ return get_inheritable(fd, 1);
+}
+
+static int
+set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ DWORD flags;
+#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
+ int request;
+ int err;
+#elif defined(HAVE_FCNTL_H)
+ int flags;
+ int res;
+#endif
+
+ /* atomic_flag_works can only be used to make the file descriptor
+ non-inheritable */
+ assert(!(atomic_flag_works != NULL && inheritable));
+
+ if (atomic_flag_works != NULL && !inheritable) {
+ if (*atomic_flag_works == -1) {
+ int inheritable = get_inheritable(fd, raise);
+ if (inheritable == -1)
+ return -1;
+ *atomic_flag_works = !inheritable;
+ }
+
+ if (*atomic_flag_works)
+ return 0;
+ }
+
+#ifdef MS_WINDOWS
+ if (!_PyVerify_fd(fd)) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ if (inheritable)
+ flags = HANDLE_FLAG_INHERIT;
+ else
+ flags = 0;
+ if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+ return 0;
+
+#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
+ if (inheritable)
+ request = FIONCLEX;
+ else
+ request = FIOCLEX;
+ err = ioctl(fd, request);
+ if (err) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return 0;
+
+#else
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ if (inheritable)
+ flags &= ~FD_CLOEXEC;
+ else
+ flags |= FD_CLOEXEC;
+ res = fcntl(fd, F_SETFD, flags);
+ if (res < 0) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+/* Make the file descriptor non-inheritable.
+ Return 0 success, set errno and return -1 on error. */
+static int
+make_non_inheritable(int fd)
+{
+ return set_inheritable(fd, 0, 0, NULL);
+}
+/* Set the inheritable flag of the specified file descriptor.
+ On success: return 0, on error: raise an exception if raise is nonzero
+ and return -1.
+
+ If atomic_flag_works is not NULL:
+
+ * if *atomic_flag_works==-1, check if the inheritable is set on the file
+ descriptor: if yes, set *atomic_flag_works to 1, otherwise set to 0 and
+ set the inheritable flag
+ * if *atomic_flag_works==1: do nothing
+ * if *atomic_flag_works==0: set inheritable flag to False
+
+ Set atomic_flag_works to NULL if no atomic flag was used to create the
+ file descriptor.
+
+ atomic_flag_works can only be used to make a file descriptor
+ non-inheritable: atomic_flag_works must be NULL if inheritable=1. */
+int
+_Py_set_inheritable(int fd, int inheritable, int *atomic_flag_works)
+{
+ return set_inheritable(fd, inheritable, 1, atomic_flag_works);
+}
+
+/* Open a file with the specified flags (wrapper to open() function).
+ The file descriptor is created non-inheritable. */
+int
+_Py_open(const char *pathname, int flags)
+{
+ int fd;
+#ifdef MS_WINDOWS
+ fd = open(pathname, flags | O_NOINHERIT);
+ if (fd < 0)
+ return fd;
+#else
+
+ int *atomic_flag_works;
+#ifdef O_CLOEXEC
+ atomic_flag_works = &_Py_open_cloexec_works;
+ flags |= O_CLOEXEC;
+#else
+ atomic_flag_works = NULL;
+#endif
+ fd = open(pathname, flags);
+ if (fd < 0)
+ return fd;
+
+ if (set_inheritable(fd, 0, 0, atomic_flag_works) < 0) {
+ close(fd);
+ return -1;
+ }
+#endif /* !MS_WINDOWS */
+ return fd;
+}
+
+/* Open a file. Use _wfopen() on Windows, encode the path to the locale
+ encoding and use fopen() otherwise. The file descriptor is created
+ non-inheritable. */
FILE *
_Py_wfopen(const wchar_t *path, const wchar_t *mode)
{
-#ifndef MS_WINDOWS
FILE *f;
+#ifndef MS_WINDOWS
char *cpath;
char cmode[10];
size_t r;
@@ -568,21 +788,42 @@ _Py_wfopen(const wchar_t *path, const wchar_t *mode)
return NULL;
f = fopen(cpath, cmode);
PyMem_Free(cpath);
- return f;
#else
- return _wfopen(path, mode);
+ f = _wfopen(path, mode);
#endif
+ if (f == NULL)
+ return NULL;
+ if (make_non_inheritable(fileno(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ return f;
}
-/* Call _wfopen() on Windows, or encode the path to the filesystem encoding and
- call fopen() otherwise.
+/* Wrapper to fopen(). The file descriptor is created non-inheritable. */
+FILE*
+_Py_fopen(const char *pathname, const char *mode)
+{
+ FILE *f = fopen(pathname, mode);
+ if (f == NULL)
+ return NULL;
+ if (make_non_inheritable(fileno(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ return f;
+}
- Return the new file object on success, or NULL if the file cannot be open or
- (if PyErr_Occurred()) on unicode error */
+/* Open a file. Call _wfopen() on Windows, or encode the path to the filesystem
+ encoding and call fopen() otherwise. The file descriptor is created
+ non-inheritable.
+ Return the new file object on success, or NULL if the file cannot be open or
+ (if PyErr_Occurred()) on unicode error. */
FILE*
-_Py_fopen(PyObject *path, const char *mode)
+_Py_fopen_obj(PyObject *path, const char *mode)
{
+ FILE *f;
#ifdef MS_WINDOWS
wchar_t *wpath;
wchar_t wmode[10];
@@ -602,16 +843,21 @@ _Py_fopen(PyObject *path, const char *mode)
if (usize == 0)
return NULL;
- return _wfopen(wpath, wmode);
+ f = _wfopen(wpath, wmode);
#else
- FILE *f;
PyObject *bytes;
if (!PyUnicode_FSConverter(path, &bytes))
return NULL;
f = fopen(PyBytes_AS_STRING(bytes), mode);
Py_DECREF(bytes);
- return f;
#endif
+ if (f == NULL)
+ return NULL;
+ if (make_non_inheritable(fileno(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ return f;
}
#ifdef HAVE_READLINK
@@ -729,3 +975,72 @@ _Py_wgetcwd(wchar_t *buf, size_t size)
#endif
}
+/* Duplicate a file descriptor. The new file descriptor is created as
+ non-inheritable. Return a new file descriptor on success, raise an OSError
+ exception and return -1 on error.
+
+ The GIL is released to call dup(). The caller must hold the GIL. */
+int
+_Py_dup(int fd)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ DWORD ftype;
+#endif
+
+ if (!_PyVerify_fd(fd)) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+#ifdef MS_WINDOWS
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (handle == INVALID_HANDLE_VALUE) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+
+ /* get the file type, ignore the error if it failed */
+ ftype = GetFileType(handle);
+
+ Py_BEGIN_ALLOW_THREADS
+ fd = dup(fd);
+ Py_END_ALLOW_THREADS
+ if (fd < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ /* Character files like console cannot be make non-inheritable */
+ if (ftype != FILE_TYPE_CHAR) {
+ if (_Py_set_inheritable(fd, 0, NULL) < 0) {
+ close(fd);
+ return -1;
+ }
+ }
+#elif defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC)
+ Py_BEGIN_ALLOW_THREADS
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ Py_END_ALLOW_THREADS
+ if (fd < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+#else
+ Py_BEGIN_ALLOW_THREADS
+ fd = dup(fd);
+ Py_END_ALLOW_THREADS
+ if (fd < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ if (_Py_set_inheritable(fd, 0, NULL) < 0) {
+ close(fd);
+ return -1;
+ }
+#endif
+ return fd;
+}
+
diff --git a/Python/import.c b/Python/import.c
index b18ba2cb98..1a162ee711 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -1797,7 +1797,7 @@ imp_load_dynamic(PyObject *self, PyObject *args)
&name, PyUnicode_FSDecoder, &pathname, &fob))
return NULL;
if (fob != NULL) {
- fp = _Py_fopen(pathname, "r");
+ fp = _Py_fopen_obj(pathname, "r");
if (fp == NULL) {
Py_DECREF(pathname);
if (!PyErr_Occurred())
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index dc8f412cb4..522a05d8b4 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1454,7 +1454,7 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
/* Try to run a pyc file. First, re-open in binary */
if (closeit)
fclose(fp);
- if ((pyc_fp = fopen(filename, "rb")) == NULL) {
+ if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) {
fprintf(stderr, "python: Can't reopen .pyc file\n");
goto done;
}
diff --git a/Python/random.c b/Python/random.c
index 709f980dca..8cf2fef7c6 100644
--- a/Python/random.c
+++ b/Python/random.c
@@ -101,7 +101,7 @@ dev_urandom_noraise(char *buffer, Py_ssize_t size)
assert (0 < size);
- fd = open("/dev/urandom", O_RDONLY);
+ fd = _Py_open("/dev/urandom", O_RDONLY);
if (fd < 0)
Py_FatalError("Failed to open /dev/urandom");
@@ -134,7 +134,7 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
return 0;
Py_BEGIN_ALLOW_THREADS
- fd = open("/dev/urandom", O_RDONLY);
+ fd = _Py_open("/dev/urandom", O_RDONLY);
Py_END_ALLOW_THREADS
if (fd < 0)
{