33#include <locale.h>
44
55#ifdef MS_WINDOWS
6+ # include <malloc.h>
67# include <windows.h>
78#endif
89
@@ -636,14 +637,10 @@ _Py_fstat(int fd, struct _Py_stat_struct *result)
636637 else
637638 h = (HANDLE )_get_osfhandle (fd );
638639
639- /* Protocol violation: we explicitly clear errno, instead of
640- setting it to a POSIX error. Callers should use GetLastError. */
641640 errno = 0 ;
642641
643642 if (h == INVALID_HANDLE_VALUE ) {
644- /* This is really a C library error (invalid file handle).
645- We set the Win32 error to the closes one matching. */
646- SetLastError (ERROR_INVALID_HANDLE );
643+ errno = EBADF ;
647644 return -1 ;
648645 }
649646 memset (result , 0 , sizeof (* result ));
@@ -652,6 +649,7 @@ _Py_fstat(int fd, struct _Py_stat_struct *result)
652649 if (type == FILE_TYPE_UNKNOWN ) {
653650 DWORD error = GetLastError ();
654651 if (error != 0 ) {
652+ errno = EINVAL ;
655653 return -1 ;
656654 }
657655 /* else: valid but unknown file */
@@ -666,6 +664,7 @@ _Py_fstat(int fd, struct _Py_stat_struct *result)
666664 }
667665
668666 if (!GetFileInformationByHandle (h , & info )) {
667+ errno = EINVAL ;
669668 return -1 ;
670669 }
671670
@@ -1267,3 +1266,102 @@ _Py_set_blocking(int fd, int blocking)
12671266}
12681267#endif
12691268
1269+ #ifdef _MSC_VER
1270+ #if _MSC_VER >= 1900
1271+
1272+ /* This function lets the Windows CRT validate the file handle without
1273+ terminating the process if it's invalid. */
1274+ int
1275+ _PyVerify_fd (int fd )
1276+ {
1277+ intptr_t osh ;
1278+ /* Fast check for the only condition we know */
1279+ if (fd < 0 ) {
1280+ _set_errno (EBADF );
1281+ return 0 ;
1282+ }
1283+ osh = _get_osfhandle (fd );
1284+ return osh != (intptr_t )-1 ;
1285+ }
1286+
1287+ #elif _MSC_VER >= 1400
1288+ /* Legacy implementation of _PyVerify_fd while transitioning to
1289+ * MSVC 14.0. This should eventually be removed. (issue23524)
1290+ */
1291+
1292+ /* Microsoft CRT in VS2005 and higher will verify that a filehandle is
1293+ * valid and raise an assertion if it isn't.
1294+ * Normally, an invalid fd is likely to be a C program error and therefore
1295+ * an assertion can be useful, but it does contradict the POSIX standard
1296+ * which for write(2) states:
1297+ * "Otherwise, -1 shall be returned and errno set to indicate the error."
1298+ * "[EBADF] The fildes argument is not a valid file descriptor open for
1299+ * writing."
1300+ * Furthermore, python allows the user to enter any old integer
1301+ * as a fd and should merely raise a python exception on error.
1302+ * The Microsoft CRT doesn't provide an official way to check for the
1303+ * validity of a file descriptor, but we can emulate its internal behaviour
1304+ * by using the exported __pinfo data member and knowledge of the
1305+ * internal structures involved.
1306+ * The structures below must be updated for each version of visual studio
1307+ * according to the file internal.h in the CRT source, until MS comes
1308+ * up with a less hacky way to do this.
1309+ * (all of this is to avoid globally modifying the CRT behaviour using
1310+ * _set_invalid_parameter_handler() and _CrtSetReportMode())
1311+ */
1312+ /* The actual size of the structure is determined at runtime.
1313+ * Only the first items must be present.
1314+ */
1315+ typedef struct {
1316+ intptr_t osfhnd ;
1317+ char osfile ;
1318+ } my_ioinfo ;
1319+
1320+ extern __declspec(dllimport ) char * __pioinfo [];
1321+ #define IOINFO_L2E 5
1322+ #define IOINFO_ARRAYS 64
1323+ #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
1324+ #define _NHANDLE_ (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS)
1325+ #define FOPEN 0x01
1326+ #define _NO_CONSOLE_FILENO (intptr_t)-2
1327+
1328+ /* This function emulates what the windows CRT does to validate file handles */
1329+ int
1330+ _PyVerify_fd (int fd )
1331+ {
1332+ const int i1 = fd >> IOINFO_L2E ;
1333+ const int i2 = fd & ((1 << IOINFO_L2E ) - 1 );
1334+
1335+ static size_t sizeof_ioinfo = 0 ;
1336+
1337+ /* Determine the actual size of the ioinfo structure,
1338+ * as used by the CRT loaded in memory
1339+ */
1340+ if (sizeof_ioinfo == 0 && __pioinfo [0 ] != NULL ) {
1341+ sizeof_ioinfo = _msize (__pioinfo [0 ]) / IOINFO_ARRAY_ELTS ;
1342+ }
1343+ if (sizeof_ioinfo == 0 ) {
1344+ /* This should not happen... */
1345+ goto fail ;
1346+ }
1347+
1348+ /* See that it isn't a special CLEAR fileno */
1349+ if (fd != _NO_CONSOLE_FILENO ) {
1350+ /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that. Instead
1351+ * we check pointer validity and other info
1352+ */
1353+ if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo [i1 ] != NULL ) {
1354+ /* finally, check that the file is open */
1355+ my_ioinfo * info = (my_ioinfo * )(__pioinfo [i1 ] + i2 * sizeof_ioinfo );
1356+ if (info -> osfile & FOPEN ) {
1357+ return 1 ;
1358+ }
1359+ }
1360+ }
1361+ fail :
1362+ errno = EBADF ;
1363+ return 0 ;
1364+ }
1365+
1366+ #endif /* _MSC_VER >= 1900 || _MSC_VER >= 1400 */
1367+ #endif /* defined _MSC_VER */
0 commit comments