Skip to content

Commit 5e6aa97

Browse files
committed
[2.7] bpo-24658: Fix read/write greater than 2 GiB on macOS (pythonGH-1705)
On macOS, fix reading from and writing into a file with a size larger than 2 GiB.
1 parent 4a59c96 commit 5e6aa97

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

Lib/test/test_largefile.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import sys
99
import unittest
1010
from test.test_support import run_unittest, TESTFN, verbose, requires, \
11-
unlink
11+
unlink, bigmemtest
1212
import io # C implementation of io
1313
import _pyio as pyio # Python implementation of io
1414

@@ -48,6 +48,15 @@ def test_seek(self):
4848
print('check file size with os.fstat')
4949
self.assertEqual(os.fstat(f.fileno())[stat.ST_SIZE], size+1)
5050

51+
# _pyio.FileIO.readall() uses a temporary bytearry then casted to bytes,
52+
# so memuse=2 is needed
53+
@bigmemtest(minsize=size, memuse=2)
54+
def test_large_read(self, _size):
55+
# bpo-24658: Test that a read greater than 2GB does not fail.
56+
with self.open(TESTFN, "rb") as f:
57+
self.assertEqual(len(f.read()), size + 1)
58+
self.assertEqual(f.tell(), size + 1)
59+
5160
def test_osstat(self):
5261
if verbose:
5362
print('check file size with os.stat')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
On macOS, fix reading from and writing into a file with a size larger than 2
2+
GiB.

Modules/_io/fileio.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@
3737
#include <windows.h>
3838
#endif
3939

40+
#if defined (MS_WINDOWS) || defined(__APPLE__)
41+
/* On Windows, the count parameter of read() is an int (bpo-9015, bpo-9611).
42+
On macOS 10.13, read() and write() with more than INT_MAX bytes
43+
fail with EINVAL (bpo-24658). */
44+
# define _PY_READ_MAX INT_MAX
45+
# define _PY_WRITE_MAX INT_MAX
46+
#else
47+
/* write() should truncate the input to PY_SSIZE_T_MAX bytes,
48+
but it's safer to do it ourself to have a portable behaviour */
49+
# define _PY_READ_MAX PY_SSIZE_T_MAX
50+
# define _PY_WRITE_MAX PY_SSIZE_T_MAX
51+
#endif
52+
4053
#if BUFSIZ < (8*1024)
4154
#define SMALLCHUNK (8*1024)
4255
#elif (BUFSIZ >= (2 << 25))
@@ -604,9 +617,9 @@ fileio_readall(fileio *self)
604617
Py_BEGIN_ALLOW_THREADS
605618
errno = 0;
606619
n = newsize - total;
620+
if (n > _PY_READ_MAX)
621+
n = _PY_READ_MAX;
607622
#if defined(MS_WIN64) || defined(MS_WINDOWS)
608-
if (n > INT_MAX)
609-
n = INT_MAX;
610623
n = read(self->fd,
611624
PyBytes_AS_STRING(result) + total,
612625
(int)n);
@@ -668,10 +681,10 @@ fileio_read(fileio *self, PyObject *args)
668681
return fileio_readall(self);
669682
}
670683

671-
#if defined(MS_WIN64) || defined(MS_WINDOWS)
672-
if (size > INT_MAX)
673-
size = INT_MAX;
674-
#endif
684+
if (size > _PY_READ_MAX) {
685+
size = _PY_READ_MAX;
686+
}
687+
675688
bytes = PyBytes_FromStringAndSize(NULL, size);
676689
if (bytes == NULL)
677690
return NULL;
@@ -723,9 +736,9 @@ fileio_write(fileio *self, PyObject *args)
723736
Py_BEGIN_ALLOW_THREADS
724737
errno = 0;
725738
len = pbuf.len;
739+
if (len > _PY_WRITE_MAX)
740+
len = _PY_WRITE_MAX;
726741
#if defined(MS_WIN64) || defined(MS_WINDOWS)
727-
if (len > INT_MAX)
728-
len = INT_MAX;
729742
n = write(self->fd, pbuf.buf, (int)len);
730743
#else
731744
n = write(self->fd, pbuf.buf, len);

0 commit comments

Comments
 (0)