diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 440cd6c1cf7..8ff0296fad0 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -591,6 +591,14 @@ class StatAttributeTests(unittest.TestCase): result = os.stat(fname) self.assertNotEqual(result.st_size, 0) + @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") + def test_stat_block_device(self): + # bpo-38030: os.stat fails for block devices + # Test a filename like "//./C:" + fname = "//./" + os.path.splitdrive(os.getcwd())[0] + result = os.stat(fname) + self.assertEqual(result.st_mode, stat.S_IFBLK) + class UtimeTests(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Windows/2019-09-04-14-01-08.bpo-38030._USdtk.rst b/Misc/NEWS.d/next/Windows/2019-09-04-14-01-08.bpo-38030._USdtk.rst new file mode 100644 index 00000000000..f1be8a1e1c8 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2019-09-04-14-01-08.bpo-38030._USdtk.rst @@ -0,0 +1 @@ +Fixes :func:`os.stat` failing for block devices on Windows diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index c412d07a0e1..81704eec321 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1794,13 +1794,13 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, case ERROR_INVALID_PARAMETER: case ERROR_INVALID_FUNCTION: case ERROR_NOT_SUPPORTED: - retval = -1; + /* Volumes and physical disks are block devices, e.g. + \\.\C: and \\.\PhysicalDrive0. */ + memset(result, 0, sizeof(*result)); + result->st_mode = 0x6000; /* S_IFBLK */ goto cleanup; } - /* Volumes and physical disks are block devices, e.g. - \\.\C: and \\.\PhysicalDrive0. */ - memset(result, 0, sizeof(*result)); - result->st_mode = 0x6000; /* S_IFBLK */ + retval = -1; goto cleanup; } } @@ -1827,7 +1827,14 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, cleanup: if (hFile != INVALID_HANDLE_VALUE) { - CloseHandle(hFile); + /* Preserve last error if we are failing */ + error = retval ? GetLastError() : 0; + if (!CloseHandle(hFile)) { + retval = -1; + } else if (retval) { + /* Restore last error */ + SetLastError(error); + } } return retval;