diff options
author | Kristján Valur Jónsson <kristjan@ccpgames.com> | 2009-02-10 13:32:24 (GMT) |
---|---|---|
committer | Kristján Valur Jónsson <kristjan@ccpgames.com> | 2009-02-10 13:32:24 (GMT) |
commit | 6a743d3694fb5138f5eab393c172c3b6789b0383 (patch) | |
tree | bf47b632ad93844f4d50d35df88c38b70ee731f9 /Modules/posixmodule.c | |
parent | e71fc8711289a2f748f6dd207d3953622a30abf7 (diff) | |
download | cpython-6a743d3694fb5138f5eab393c172c3b6789b0383.zip cpython-6a743d3694fb5138f5eab393c172c3b6789b0383.tar.gz cpython-6a743d3694fb5138f5eab393c172c3b6789b0383.tar.bz2 |
Issue 4804. Add a function to test the validity of file descriptors on Windows, and stop using global runtime settings to silence the warnings / assertions.
Diffstat (limited to 'Modules/posixmodule.c')
-rw-r--r-- | Modules/posixmodule.c | 126 |
1 files changed, 125 insertions, 1 deletions
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index cda8135..4f27cd1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -343,6 +343,109 @@ extern int lstat(const char *, struct stat *); #endif #endif +#if defined _MSC_VER && _MSC_VER >= 1400 +/* Microsoft CRT in VS2005 and higher will verify that a filehandle is + * valid and throw an assertion if it isn't. + * Normally, an invalid fd is likely to be a C program error and therefore + * an assertion can be useful, but it does contradict the POSIX standard + * which for write(2) states: + * "Otherwise, -1 shall be returned and errno set to indicate the error." + * "[EBADF] The fildes argument is not a valid file descriptor open for + * writing." + * Furthermore, python allows the user to enter any old integer + * as a fd and should merely raise a python exception on error. + * The Microsoft CRT doesn't provide an official way to check for the + * validity of a file descriptor, but we can emulate its internal behaviour + * by using the exported __pinfo data member and knowledge of the + * internal structures involved. + * The structures below must be updated for each version of visual studio + * according to the file internal.h in the CRT source, until MS comes + * up with a less hacky way to do this. + * (all of this is to avoid globally modifying the CRT behaviour using + * _set_invalid_parameter_handler() and _CrtSetReportMode()) + */ +#if _MSC_VER >= 1500 /* VS 2008 */ +typedef struct { + intptr_t osfhnd; + char osfile; + char pipech; + int lockinitflag; + CRITICAL_SECTION lock; +#ifndef _SAFECRT_IMPL + char textmode : 7; + char unicode : 1; + char pipech2[2]; + __int64 startpos; + BOOL utf8translations; + char dbcsBuffer; + BOOL dbcsBufferUsed; +#endif /* _SAFECRT_IMPL */ + } ioinfo; +#elif _MSC_VER >= 1400 /* VS 2005 */ +typedef struct { + intptr_t osfhnd; + char osfile; + char pipech; + int lockinitflag; + CRITICAL_SECTION lock; +#ifndef _SAFECRT_IMPL + char textmode : 7; + char unicode : 1; + char pipech2[2]; + __int64 startpos; + BOOL utf8translations; +#endif /* _SAFECRT_IMPL */ + } ioinfo; +#endif + +extern __declspec(dllimport) ioinfo * __pioinfo[]; +#define IOINFO_L2E 5 +#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) +#define IOINFO_ARRAYS 64 +#define _NHANDLE_ (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS) +#define FOPEN 0x01 +#define _NO_CONSOLE_FILENO (intptr_t)-2 + +/* This function emulates what the windows CRT does to validate file handles */ +int +_PyVerify_fd(int fd) +{ + const int i1 = fd >> IOINFO_L2E; + const int i2 = fd & ((1 << IOINFO_L2E) - 1); + + /* See that it isn't a special CLEAR fileno */ + if (fd != _NO_CONSOLE_FILENO) { + /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that. Instead + * we check pointer validity and other info + */ + if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) { + /* finally, check that the file is open */ + if (__pioinfo[i1][i2].osfile & FOPEN) + return 1; + } + } + errno = EBADF; + return 0; +} + +/* the special case of checking dup2. The target fd must be in a sensible range */ +static int +_PyVerify_fd_dup2(int fd1, int fd2) +{ + if (!_PyVerify_fd(fd1)) + return 0; + if (fd2 == _NO_CONSOLE_FILENO) + return 0; + if ((unsigned)fd2 < _NHANDLE_) + return 1; + else + return 0; +} +#else +/* dummy version. _PyVerify_fd() is already defined in fileobject.h */ +#define _PyVerify_fd_dup2(A, B) (1) +#endif + /* Return a dictionary corresponding to the POSIX environment table */ #ifdef WITH_NEXT_FRAMEWORK /* On Darwin/MacOSX a shared library or framework has no access to @@ -581,6 +684,8 @@ posix_fildes(PyObject *fdobj, int (*func)(int)) fd = PyObject_AsFileDescriptor(fdobj); if (fd < 0) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = (*func)(fd); Py_END_ALLOW_THREADS @@ -6188,6 +6293,8 @@ posix_close(PyObject *self, PyObject *args) int fd, res; if (!PyArg_ParseTuple(args, "i:close", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = close(fd); Py_END_ALLOW_THREADS @@ -6210,7 +6317,8 @@ posix_closerange(PyObject *self, PyObject *args) return NULL; Py_BEGIN_ALLOW_THREADS for (i = fd_from; i < fd_to; i++) - close(i); + if (_PyVerify_fd(i)) + close(i); Py_END_ALLOW_THREADS Py_RETURN_NONE; } @@ -6226,6 +6334,8 @@ posix_dup(PyObject *self, PyObject *args) int fd; if (!PyArg_ParseTuple(args, "i:dup", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS fd = dup(fd); Py_END_ALLOW_THREADS @@ -6245,6 +6355,8 @@ posix_dup2(PyObject *self, PyObject *args) int fd, fd2, res; if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) return NULL; + if (!_PyVerify_fd_dup2(fd, fd2)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = dup2(fd, fd2); Py_END_ALLOW_THREADS @@ -6289,6 +6401,8 @@ posix_lseek(PyObject *self, PyObject *args) if (PyErr_Occurred()) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS #if defined(MS_WIN64) || defined(MS_WINDOWS) res = _lseeki64(fd, pos, how); @@ -6325,6 +6439,8 @@ posix_read(PyObject *self, PyObject *args) buffer = PyString_FromStringAndSize((char *)NULL, size); if (buffer == NULL) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS n = read(fd, PyString_AsString(buffer), size); Py_END_ALLOW_THREADS @@ -6351,6 +6467,8 @@ posix_write(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "is*:write", &fd, &pbuf)) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS size = write(fd, pbuf.buf, (size_t)pbuf.len); Py_END_ALLOW_THREADS @@ -6377,6 +6495,8 @@ posix_fstat(PyObject *self, PyObject *args) /* on OpenVMS we must ensure that all bytes are written to the file */ fsync(fd); #endif + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = FSTAT(fd, &st); Py_END_ALLOW_THREADS @@ -6419,6 +6539,8 @@ posix_fdopen(PyObject *self, PyObject *args) PyMem_FREE(mode); return NULL; } + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS #if !defined(MS_WINDOWS) && defined(HAVE_FCNTL_H) if (mode[0] == 'a') { @@ -6458,6 +6580,8 @@ posix_isatty(PyObject *self, PyObject *args) int fd; if (!PyArg_ParseTuple(args, "i:isatty", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return PyBool_FromLong(0); return PyBool_FromLong(isatty(fd)); } |