mirror of
https://github.com/python/cpython.git
synced 2025-07-09 20:35:26 +00:00
Issue #18571: Implementation of the PEP 446: file descriptors and file handles
are now created non-inheritable; add functions os.get/set_inheritable(), os.get/set_handle_inheritable() and socket.socket.get/set_inheritable().
This commit is contained in:
parent
46e1ce214b
commit
daf455554b
51 changed files with 1448 additions and 317 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
(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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue