[3.6] bpo-24658: Fix read/write greater than 2 GiB on macOS (GH-1705)… · python/cpython@a5ebc20 (original) (raw)
5 files changed
lines changed
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -29,6 +29,19 @@ PyAPI_FUNC(char*) _Py_EncodeLocaleEx( | ||
29 | 29 | |
30 | 30 | PyAPI_FUNC(PyObject *) _Py_device_encoding(int); |
31 | 31 | |
32 | +#if defined(MS_WINDOWS) | | |
33 | +/* On Windows, the count parameter of read() is an int (bpo-9015, bpo-9611). | |
34 | + On macOS 10.13, read() and write() with more than INT_MAX bytes | |
35 | + fail with EINVAL (bpo-24658). */ | |
36 | +# define _PY_READ_MAX INT_MAX | |
37 | +# define _PY_WRITE_MAX INT_MAX | |
38 | +#else | |
39 | +/* write() should truncate the input to PY_SSIZE_T_MAX bytes, | |
40 | + but it's safer to do it ourself to have a portable behaviour */ | |
41 | +# define _PY_READ_MAX PY_SSIZE_T_MAX | |
42 | +# define _PY_WRITE_MAX PY_SSIZE_T_MAX | |
43 | +#endif | |
44 | + | |
32 | 45 | #ifdef MS_WINDOWS |
33 | 46 | struct _Py_stat_struct { |
34 | 47 | unsigned long st_dev; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -5,12 +5,12 @@ | ||
5 | 5 | import stat |
6 | 6 | import sys |
7 | 7 | import unittest |
8 | -from test.support import TESTFN, requires, unlink | |
8 | +from test.support import TESTFN, requires, unlink, bigmemtest | |
9 | 9 | import io # C implementation of io |
10 | 10 | import _pyio as pyio # Python implementation of io |
11 | 11 | |
12 | -# size of file to create (>2GB; 2GB == 2147483648 bytes) | |
13 | -size = 2500000000 | |
12 | +# size of file to create (>2 GiB; 2 GiB == 2,147,483,648 bytes) | |
13 | +size = 2_500_000_000 | |
14 | 14 | |
15 | 15 | class LargeFileTest: |
16 | 16 | """Test that each file function works as expected for large |
@@ -45,6 +45,15 @@ def tearDownClass(cls): | ||
45 | 45 | raise cls.failureException('File was not truncated by opening ' |
46 | 46 | 'with mode "wb"') |
47 | 47 | |
48 | +# _pyio.FileIO.readall() uses a temporary bytearray then casted to bytes, | |
49 | +# so memuse=2 is needed | |
50 | +@bigmemtest(size=size, memuse=2, dry_run=False) | |
51 | +def test_large_read(self, _size): | |
52 | +# bpo-24658: Test that a read greater than 2GB does not fail. | |
53 | +with self.open(TESTFN, "rb") as f: | |
54 | +self.assertEqual(len(f.read()), size + 1) | |
55 | +self.assertEqual(f.tell(), size + 1) | |
56 | + | |
48 | 57 | def test_osstat(self): |
49 | 58 | self.assertEqual(os.stat(TESTFN)[stat.ST_SIZE], size+1) |
50 | 59 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
1 | +On macOS, fix reading from and writing into a file with a size larger than 2 GiB. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -799,11 +799,9 @@ _io_FileIO_read_impl(fileio *self, Py_ssize_t size) | ||
799 | 799 | if (size < 0) |
800 | 800 | return _io_FileIO_readall_impl(self); |
801 | 801 | |
802 | -#ifdef MS_WINDOWS | |
803 | -/* On Windows, the count parameter of read() is an int */ | |
804 | -if (size > INT_MAX) | |
805 | -size = INT_MAX; | |
806 | -#endif | |
802 | +if (size > _PY_READ_MAX) { | |
803 | +size = _PY_READ_MAX; | |
804 | + } | |
807 | 805 | |
808 | 806 | bytes = PyBytes_FromStringAndSize(NULL, size); |
809 | 807 | if (bytes == NULL) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1263,18 +1263,9 @@ _Py_read(int fd, void *buf, size_t count) | ||
1263 | 1263 | * handler raised an exception. */ |
1264 | 1264 | assert(!PyErr_Occurred()); |
1265 | 1265 | |
1266 | -#ifdef MS_WINDOWS | |
1267 | -if (count > INT_MAX) { | |
1268 | -/* On Windows, the count parameter of read() is an int */ | |
1269 | -count = INT_MAX; | |
1270 | - } | |
1271 | -#else | |
1272 | -if (count > PY_SSIZE_T_MAX) { | |
1273 | -/* if count is greater than PY_SSIZE_T_MAX, | |
1274 | - * read() result is undefined */ | |
1275 | -count = PY_SSIZE_T_MAX; | |
1266 | +if (count > _PY_READ_MAX) { | |
1267 | +count = _PY_READ_MAX; | |
1276 | 1268 | } |
1277 | -#endif | |
1278 | 1269 | |
1279 | 1270 | _Py_BEGIN_SUPPRESS_IPH |
1280 | 1271 | do { |
@@ -1325,15 +1316,10 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) | ||
1325 | 1316 | depending on heap usage). */ |
1326 | 1317 | count = 32767; |
1327 | 1318 | } |
1328 | -else if (count > INT_MAX) | |
1329 | -count = INT_MAX; | |
1330 | -#else | |
1331 | -if (count > PY_SSIZE_T_MAX) { | |
1332 | -/* write() should truncate count to PY_SSIZE_T_MAX, but it's safer | |
1333 | - * to do it ourself to have a portable behaviour. */ | |
1334 | -count = PY_SSIZE_T_MAX; | |
1335 | - } | |
1336 | 1319 | #endif |
1320 | +if (count > _PY_WRITE_MAX) { | |
1321 | +count = _PY_WRITE_MAX; | |
1322 | + } | |
1337 | 1323 | |
1338 | 1324 | if (gil_held) { |
1339 | 1325 | do { |