Skip to content

Commit a555cfc

Browse files
committed
Issue python#23694: Enhance _Py_open(), it now raises exceptions
* _Py_open() now raises exceptions on error. If open() fails, it raises an OSError with the filename. * _Py_open() now releases the GIL while calling open() * Add _Py_open_noraise() when _Py_open() cannot be used because the GIL is not held
1 parent 6562b29 commit a555cfc

File tree

8 files changed

+71
-45
lines changed

8 files changed

+71
-45
lines changed

Include/fileutils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ PyAPI_FUNC(int) _Py_stat(
6262
PyAPI_FUNC(int) _Py_open(
6363
const char *pathname,
6464
int flags);
65+
66+
PyAPI_FUNC(int) _Py_open_noraise(
67+
const char *pathname,
68+
int flags);
6569
#endif
6670

6771
PyAPI_FUNC(FILE *) _Py_wfopen(

Modules/_posixsubprocess.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ _close_open_fds_safe(int start_fd, PyObject* py_fds_to_keep)
257257
fd_dir_fd = _Py_open(FD_DIR, O_RDONLY);
258258
if (fd_dir_fd == -1) {
259259
/* No way to get a list of open fds. */
260+
PyErr_Clear();
260261
_close_fds_by_brute_force(start_fd, py_fds_to_keep);
261262
return;
262263
} else {

Modules/mmapmodule.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,6 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
12211221
fd = devzero = _Py_open("/dev/zero", O_RDWR);
12221222
if (devzero == -1) {
12231223
Py_DECREF(m_obj);
1224-
PyErr_SetFromErrno(PyExc_OSError);
12251224
return NULL;
12261225
}
12271226
#endif

Modules/ossaudiodev.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,8 @@ newossobject(PyObject *arg)
116116
provides a special ioctl() for non-blocking read/write, which is
117117
exposed via oss_nonblock() below. */
118118
fd = _Py_open(devicename, imode|O_NONBLOCK);
119-
120-
if (fd == -1) {
121-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
119+
if (fd == -1)
122120
return NULL;
123-
}
124121

125122
/* And (try to) put it back in blocking mode so we get the
126123
expected write() semantics. */
@@ -180,10 +177,8 @@ newossmixerobject(PyObject *arg)
180177
}
181178

182179
fd = _Py_open(devicename, O_RDWR);
183-
if (fd == -1) {
184-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, devicename);
180+
if (fd == -1)
185181
return NULL;
186-
}
187182

188183
if ((self = PyObject_New(oss_mixer_t, &OSSMixerType)) == NULL) {
189184
close(fd);

Modules/posixmodule.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7930,7 +7930,7 @@ os_openpty_impl(PyModuleDef *module)
79307930

79317931
slave_fd = _Py_open(slave_name, O_RDWR);
79327932
if (slave_fd < 0)
7933-
goto posix_error;
7933+
goto error;
79347934

79357935
#else
79367936
master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */
@@ -7958,8 +7958,8 @@ os_openpty_impl(PyModuleDef *module)
79587958
goto posix_error;
79597959

79607960
slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */
7961-
if (slave_fd < 0)
7962-
goto posix_error;
7961+
if (slave_fd == -1)
7962+
goto error;
79637963

79647964
if (_Py_set_inheritable(master_fd, 0, NULL) < 0)
79657965
goto posix_error;
@@ -7977,9 +7977,7 @@ os_openpty_impl(PyModuleDef *module)
79777977

79787978
posix_error:
79797979
posix_error();
7980-
#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY)
79817980
error:
7982-
#endif
79837981
if (master_fd != -1)
79847982
close(master_fd);
79857983
if (slave_fd != -1)

Modules/selectmodule.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,26 +1013,21 @@ newDevPollObject(void)
10131013
struct pollfd *fds;
10141014
struct rlimit limit;
10151015

1016-
Py_BEGIN_ALLOW_THREADS
10171016
/*
10181017
** If we try to process more that getrlimit()
10191018
** fds, the kernel will give an error, so
10201019
** we set the limit here. It is a dynamic
10211020
** value, because we can change rlimit() anytime.
10221021
*/
10231022
limit_result = getrlimit(RLIMIT_NOFILE, &limit);
1024-
if (limit_result != -1)
1025-
fd_devpoll = _Py_open("/dev/poll", O_RDWR);
1026-
Py_END_ALLOW_THREADS
1027-
10281023
if (limit_result == -1) {
10291024
PyErr_SetFromErrno(PyExc_OSError);
10301025
return NULL;
10311026
}
1032-
if (fd_devpoll == -1) {
1033-
PyErr_SetFromErrnoWithFilename(PyExc_IOError, "/dev/poll");
1027+
1028+
fd_devpoll = _Py_open("/dev/poll", O_RDWR);
1029+
if (fd_devpoll == -1)
10341030
return NULL;
1035-
}
10361031

10371032
fds = PyMem_NEW(struct pollfd, limit.rlim_cur);
10381033
if (fds == NULL) {

Python/fileutils.c

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ extern wchar_t* _Py_DecodeUTF8_surrogateescape(const char *s, Py_ssize_t size);
3030
0: open() ignores O_CLOEXEC flag, ex: Linux kernel older than 2.6.23
3131
1: open() supports O_CLOEXEC flag, close-on-exec is set
3232
33-
The flag is used by _Py_open(), io.FileIO and os.open() */
33+
The flag is used by _Py_open(), _Py_open_noraise(), io.FileIO
34+
and os.open(). */
3435
int _Py_open_cloexec_works = -1;
3536
#endif
3637

@@ -907,37 +908,74 @@ _Py_set_inheritable(int fd, int inheritable, int *atomic_flag_works)
907908
return set_inheritable(fd, inheritable, 1, atomic_flag_works);
908909
}
909910

910-
/* Open a file with the specified flags (wrapper to open() function).
911-
The file descriptor is created non-inheritable. */
912-
int
913-
_Py_open(const char *pathname, int flags)
911+
static int
912+
_Py_open_impl(const char *pathname, int flags, int gil_held)
914913
{
915914
int fd;
916-
#ifdef MS_WINDOWS
917-
fd = open(pathname, flags | O_NOINHERIT);
918-
if (fd < 0)
919-
return fd;
920-
#else
921-
915+
#ifndef MS_WINDOWS
922916
int *atomic_flag_works;
923-
#ifdef O_CLOEXEC
917+
#endif
918+
919+
#ifdef MS_WINDOWS
920+
flags |= O_NOINHERIT;
921+
#elif defined(O_CLOEXEC)
924922
atomic_flag_works = &_Py_open_cloexec_works;
925923
flags |= O_CLOEXEC;
926924
#else
927925
atomic_flag_works = NULL;
928926
#endif
929-
fd = open(pathname, flags);
930-
if (fd < 0)
931-
return fd;
932927

933-
if (set_inheritable(fd, 0, 0, atomic_flag_works) < 0) {
928+
if (gil_held) {
929+
Py_BEGIN_ALLOW_THREADS
930+
fd = open(pathname, flags);
931+
Py_END_ALLOW_THREADS
932+
933+
if (fd < 0) {
934+
PyErr_SetFromErrnoWithFilename(PyExc_OSError, pathname);
935+
return -1;
936+
}
937+
}
938+
else {
939+
fd = open(pathname, flags);
940+
if (fd < 0)
941+
return -1;
942+
}
943+
944+
#ifndef MS_WINDOWS
945+
if (set_inheritable(fd, 0, gil_held, atomic_flag_works) < 0) {
934946
close(fd);
935947
return -1;
936948
}
937-
#endif /* !MS_WINDOWS */
949+
#endif
950+
938951
return fd;
939952
}
940953

954+
/* Open a file with the specified flags (wrapper to open() function).
955+
Return a file descriptor on success. Raise an exception and return -1 on
956+
error.
957+
958+
The file descriptor is created non-inheritable.
959+
960+
The GIL must be held. Use _Py_open_noraise() if the GIL cannot be held. */
961+
int
962+
_Py_open(const char *pathname, int flags)
963+
{
964+
/* _Py_open() must be called with the GIL held. */
965+
assert(PyGILState_Check());
966+
return _Py_open_impl(pathname, flags, 1);
967+
}
968+
969+
/* Open a file with the specified flags (wrapper to open() function).
970+
Return a file descriptor on success. Set errno and return -1 on error.
971+
972+
The file descriptor is created non-inheritable. */
973+
int
974+
_Py_open_noraise(const char *pathname, int flags)
975+
{
976+
return _Py_open_impl(pathname, flags, 0);
977+
}
978+
941979
/* Open a file. Use _wfopen() on Windows, encode the path to the locale
942980
encoding and use fopen() otherwise. The file descriptor is created
943981
non-inheritable. */

Python/random.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
111111

112112
assert (0 < size);
113113

114-
fd = _Py_open("/dev/urandom", O_RDONLY);
114+
fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
115115
if (fd < 0)
116116
Py_FatalError("Failed to open /dev/urandom");
117117

@@ -158,17 +158,13 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
158158
if (urandom_cache.fd >= 0)
159159
fd = urandom_cache.fd;
160160
else {
161-
Py_BEGIN_ALLOW_THREADS
162161
fd = _Py_open("/dev/urandom", O_RDONLY);
163-
Py_END_ALLOW_THREADS
164-
if (fd < 0)
165-
{
162+
if (fd < 0) {
166163
if (errno == ENOENT || errno == ENXIO ||
167164
errno == ENODEV || errno == EACCES)
168165
PyErr_SetString(PyExc_NotImplementedError,
169166
"/dev/urandom (or equivalent) not found");
170-
else
171-
PyErr_SetFromErrno(PyExc_OSError);
167+
/* otherwise, keep the OSError exception raised by _Py_open() */
172168
return -1;
173169
}
174170
if (urandom_cache.fd >= 0) {

0 commit comments

Comments
 (0)