diff options
author | Segev Finer <segev208@gmail.com> | 2021-04-23 22:00:27 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-23 22:00:27 (GMT) |
commit | 5e437fb872279960992c9a07f1a4c051b4948c53 (patch) | |
tree | da3e3ad584eba24da9cfe63c4acd1a01e55d4676 /Modules | |
parent | 6b59e662fa39a356d6eb03d349b140477857f4b1 (diff) | |
download | cpython-5e437fb872279960992c9a07f1a4c051b4948c53.zip cpython-5e437fb872279960992c9a07f1a4c051b4948c53.tar.gz cpython-5e437fb872279960992c9a07f1a4c051b4948c53.tar.bz2 |
bpo-30555: Fix WindowsConsoleIO fails in the presence of fd redirection (GH-1927)
This works by not caching the handle and instead getting the handle from
the file descriptor each time, so that if the actual handle changes by
fd redirection closing/opening the console handle beneath our feet, we
will keep working correctly.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_io/clinic/winconsoleio.c.h | 13 | ||||
-rw-r--r-- | Modules/_io/winconsoleio.c | 133 | ||||
-rw-r--r-- | Modules/mmapmodule.c | 9 | ||||
-rw-r--r-- | Modules/posixmodule.c | 6 |
4 files changed, 78 insertions, 83 deletions
diff --git a/Modules/_io/clinic/winconsoleio.c.h b/Modules/_io/clinic/winconsoleio.c.h index cf6ce60..84e73dc 100644 --- a/Modules/_io/clinic/winconsoleio.c.h +++ b/Modules/_io/clinic/winconsoleio.c.h @@ -8,10 +8,10 @@ PyDoc_STRVAR(_io__WindowsConsoleIO_close__doc__, "close($self, /)\n" "--\n" "\n" -"Close the handle.\n" +"Close the console object.\n" "\n" -"A closed handle cannot be used for further I/O operations. close() may be\n" -"called more than once without error."); +"A closed console object cannot be used for further I/O operations.\n" +"close() may be called more than once without error."); #define _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF \ {"close", (PyCFunction)_io__WindowsConsoleIO_close, METH_NOARGS, _io__WindowsConsoleIO_close__doc__}, @@ -110,10 +110,7 @@ PyDoc_STRVAR(_io__WindowsConsoleIO_fileno__doc__, "fileno($self, /)\n" "--\n" "\n" -"Return the underlying file descriptor (an integer).\n" -"\n" -"fileno is only set when a file descriptor is used to open\n" -"one of the standard streams."); +"Return the underlying file descriptor (an integer)."); #define _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF \ {"fileno", (PyCFunction)_io__WindowsConsoleIO_fileno, METH_NOARGS, _io__WindowsConsoleIO_fileno__doc__}, @@ -381,4 +378,4 @@ _io__WindowsConsoleIO_isatty(winconsoleio *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #define _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #endif /* !defined(_IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF) */ -/*[clinic end generated code: output=a28b3120fa53b256 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=48080572ffee22f5 input=a9049054013a1b77]*/ diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 4ccf027..460f2d3 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -64,10 +64,7 @@ char _PyIO_get_console_type(PyObject *path_or_fd) { int fd = PyLong_AsLong(path_or_fd); PyErr_Clear(); if (fd >= 0) { - HANDLE handle; - _Py_BEGIN_SUPPRESS_IPH - handle = (HANDLE)_get_osfhandle(fd); - _Py_END_SUPPRESS_IPH + HANDLE handle = _Py_get_osfhandle_noraise(fd); if (handle == INVALID_HANDLE_VALUE) return '\0'; return _get_console_type(handle); @@ -143,12 +140,11 @@ class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type" typedef struct { PyObject_HEAD - HANDLE handle; int fd; unsigned int created : 1; unsigned int readable : 1; unsigned int writable : 1; - unsigned int closehandle : 1; + unsigned int closefd : 1; char finalizing; unsigned int blksize; PyObject *weakreflist; @@ -164,7 +160,7 @@ _Py_IDENTIFIER(name); int _PyWindowsConsoleIO_closed(PyObject *self) { - return ((winconsoleio *)self)->handle == INVALID_HANDLE_VALUE; + return ((winconsoleio *)self)->fd == -1; } @@ -172,16 +168,12 @@ _PyWindowsConsoleIO_closed(PyObject *self) static int internal_close(winconsoleio *self) { - if (self->handle != INVALID_HANDLE_VALUE) { - if (self->closehandle) { - if (self->fd >= 0) { - _Py_BEGIN_SUPPRESS_IPH - close(self->fd); - _Py_END_SUPPRESS_IPH - } - CloseHandle(self->handle); + if (self->fd != -1) { + if (self->closefd) { + _Py_BEGIN_SUPPRESS_IPH + close(self->fd); + _Py_END_SUPPRESS_IPH } - self->handle = INVALID_HANDLE_VALUE; self->fd = -1; } return 0; @@ -190,15 +182,15 @@ internal_close(winconsoleio *self) /*[clinic input] _io._WindowsConsoleIO.close -Close the handle. +Close the console object. -A closed handle cannot be used for further I/O operations. close() may be -called more than once without error. +A closed console object cannot be used for further I/O operations. +close() may be called more than once without error. [clinic start generated code]*/ static PyObject * _io__WindowsConsoleIO_close_impl(winconsoleio *self) -/*[clinic end generated code: output=27ef95b66c29057b input=185617e349ae4c7b]*/ +/*[clinic end generated code: output=27ef95b66c29057b input=68c4e5754f8136c2]*/ { PyObject *res; PyObject *exc, *val, *tb; @@ -206,8 +198,8 @@ _io__WindowsConsoleIO_close_impl(winconsoleio *self) _Py_IDENTIFIER(close); res = _PyObject_CallMethodIdOneArg((PyObject*)&PyRawIOBase_Type, &PyId_close, (PyObject*)self); - if (!self->closehandle) { - self->handle = INVALID_HANDLE_VALUE; + if (!self->closefd) { + self->fd = -1; return res; } if (res == NULL) @@ -229,12 +221,11 @@ winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) self = (winconsoleio *) type->tp_alloc(type, 0); if (self != NULL) { - self->handle = INVALID_HANDLE_VALUE; self->fd = -1; self->created = 0; self->readable = 0; self->writable = 0; - self->closehandle = 0; + self->closefd = 0; self->blksize = 0; self->weakreflist = NULL; } @@ -269,16 +260,17 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, int rwa = 0; int fd = -1; int fd_is_own = 0; + HANDLE handle = NULL; assert(PyWindowsConsoleIO_Check(self)); - if (self->handle >= 0) { - if (self->closehandle) { + if (self->fd >= 0) { + if (self->closefd) { /* Have to close the existing file first. */ if (internal_close(self) < 0) return -1; } else - self->handle = INVALID_HANDLE_VALUE; + self->fd = -1; } fd = _PyLong_AsInt(nameobj); @@ -341,14 +333,12 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, goto bad_mode; if (fd >= 0) { - _Py_BEGIN_SUPPRESS_IPH - self->handle = (HANDLE)_get_osfhandle(fd); - _Py_END_SUPPRESS_IPH - self->closehandle = 0; + handle = _Py_get_osfhandle_noraise(fd); + self->closefd = 0; } else { DWORD access = GENERIC_READ; - self->closehandle = 1; + self->closefd = 1; if (!closefd) { PyErr_SetString(PyExc_ValueError, "Cannot use closefd=False with file name"); @@ -363,21 +353,31 @@ _io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj, on the specific access. This is required for modern names CONIN$ and CONOUT$, which allow reading/writing state as well as reading/writing content. */ - self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, + handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (self->handle == INVALID_HANDLE_VALUE) - self->handle = CreateFileW(name, access, + if (handle == INVALID_HANDLE_VALUE) + handle = CreateFileW(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); Py_END_ALLOW_THREADS - if (self->handle == INVALID_HANDLE_VALUE) { + if (handle == INVALID_HANDLE_VALUE) { PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj); goto error; } + + if (self->writable) + self->fd = _Py_open_osfhandle_noraise(handle, _O_WRONLY | _O_BINARY); + else + self->fd = _Py_open_osfhandle_noraise(handle, _O_RDONLY | _O_BINARY); + if (self->fd < 0) { + CloseHandle(handle); + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj); + goto error; + } } if (console_type == '\0') - console_type = _get_console_type(self->handle); + console_type = _get_console_type(handle); if (self->writable && console_type != 'w') { PyErr_SetString(PyExc_ValueError, @@ -460,25 +460,14 @@ _io._WindowsConsoleIO.fileno Return the underlying file descriptor (an integer). -fileno is only set when a file descriptor is used to open -one of the standard streams. - [clinic start generated code]*/ static PyObject * _io__WindowsConsoleIO_fileno_impl(winconsoleio *self) -/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/ +/*[clinic end generated code: output=006fa74ce3b5cfbf input=845c47ebbc3a2f67]*/ { - if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) { - _Py_BEGIN_SUPPRESS_IPH - if (self->writable) - self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY); - else - self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY); - _Py_END_SUPPRESS_IPH - } if (self->fd < 0) - return err_mode("fileno"); + return err_closed(); return PyLong_FromLong(self->fd); } @@ -492,7 +481,7 @@ static PyObject * _io__WindowsConsoleIO_readable_impl(winconsoleio *self) /*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/ { - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return err_closed(); return PyBool_FromLong((long) self->readable); } @@ -507,7 +496,7 @@ static PyObject * _io__WindowsConsoleIO_writable_impl(winconsoleio *self) /*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/ { - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return err_closed(); return PyBool_FromLong((long) self->writable); } @@ -642,7 +631,7 @@ error: static Py_ssize_t readinto(winconsoleio *self, char *buf, Py_ssize_t len) { - if (self->handle == INVALID_HANDLE_VALUE) { + if (self->fd == -1) { err_closed(); return -1; } @@ -657,6 +646,10 @@ readinto(winconsoleio *self, char *buf, Py_ssize_t len) return -1; } + HANDLE handle = _Py_get_osfhandle(self->fd); + if (handle == INVALID_HANDLE_VALUE) + return -1; + /* Each character may take up to 4 bytes in the final buffer. This is highly conservative, but necessary to avoid failure for any given Unicode input (e.g. \U0010ffff). @@ -678,7 +671,7 @@ readinto(winconsoleio *self, char *buf, Py_ssize_t len) return read_len; DWORD n; - wchar_t *wbuf = read_console_w(self->handle, wlen, &n); + wchar_t *wbuf = read_console_w(handle, wlen, &n); if (wbuf == NULL) return -1; if (n == 0) { @@ -784,10 +777,15 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) DWORD bufsize, n, len = 0; PyObject *bytes; DWORD bytes_size, rn; + HANDLE handle; - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return err_closed(); + handle = _Py_get_osfhandle(self->fd); + if (handle == INVALID_HANDLE_VALUE) + return NULL; + bufsize = BUFSIZ; buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t)); @@ -819,7 +817,7 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) buf = tmp; } - subbuf = read_console_w(self->handle, bufsize - len, &n); + subbuf = read_console_w(handle, bufsize - len, &n); if (subbuf == NULL) { PyMem_Free(buf); @@ -909,7 +907,7 @@ _io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size) PyObject *bytes; Py_ssize_t bytes_size; - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return err_closed(); if (!self->readable) return err_mode("reading"); @@ -959,12 +957,17 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) BOOL res = TRUE; wchar_t *wbuf; DWORD len, wlen, n = 0; + HANDLE handle; - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return err_closed(); if (!self->writable) return err_mode("writing"); + handle = _Py_get_osfhandle(self->fd); + if (handle == INVALID_HANDLE_VALUE) + return NULL; + if (!b->len) { return PyLong_FromLong(0); } @@ -995,7 +998,7 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) Py_BEGIN_ALLOW_THREADS wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen); if (wlen) { - res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL); + res = WriteConsoleW(handle, wbuf, wlen, &n, NULL); if (res && n < wlen) { /* Wrote fewer characters than expected, which means our * len value may be wrong. So recalculate it from the @@ -1027,15 +1030,15 @@ _io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b) static PyObject * winconsoleio_repr(winconsoleio *self) { - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>"); if (self->readable) return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>", - self->closehandle ? "True" : "False"); + self->closefd ? "True" : "False"); if (self->writable) return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>", - self->closehandle ? "True" : "False"); + self->closefd ? "True" : "False"); PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode"); return NULL; @@ -1051,7 +1054,7 @@ static PyObject * _io__WindowsConsoleIO_isatty_impl(winconsoleio *self) /*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/ { - if (self->handle == INVALID_HANDLE_VALUE) + if (self->fd == -1) return err_closed(); Py_RETURN_TRUE; @@ -1077,13 +1080,13 @@ static PyMethodDef winconsoleio_methods[] = { static PyObject * get_closed(winconsoleio *self, void *closure) { - return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE)); + return PyBool_FromLong((long)(self->fd == -1)); } static PyObject * get_closefd(winconsoleio *self, void *closure) { - return PyBool_FromLong((long)(self->closehandle)); + return PyBool_FromLong((long)(self->closefd)); } static PyObject * diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 1e66962..9a2542f 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -1370,13 +1370,10 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict) */ if (fileno != -1 && fileno != 0) { /* Ensure that fileno is within the CRT's valid range */ - _Py_BEGIN_SUPPRESS_IPH - fh = (HANDLE)_get_osfhandle(fileno); - _Py_END_SUPPRESS_IPH - if (fh==(HANDLE)-1) { - PyErr_SetFromErrno(PyExc_OSError); + fh = _Py_get_osfhandle(fileno); + if (fh == INVALID_HANDLE_VALUE) return NULL; - } + /* Win9x appears to need us seeked to zero */ lseek(fileno, 0, SEEK_SET); } diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e754db7..ecd210e 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -10098,18 +10098,16 @@ os_pipe_impl(PyObject *module) attr.bInheritHandle = FALSE; Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH ok = CreatePipe(&read, &write, &attr, 0); if (ok) { - fds[0] = _open_osfhandle((intptr_t)read, _O_RDONLY); - fds[1] = _open_osfhandle((intptr_t)write, _O_WRONLY); + fds[0] = _Py_open_osfhandle_noraise(read, _O_RDONLY); + fds[1] = _Py_open_osfhandle_noraise(write, _O_WRONLY); if (fds[0] == -1 || fds[1] == -1) { CloseHandle(read); CloseHandle(write); ok = 0; } } - _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (!ok) |