Skip to content

Commit 0b6806b

Browse files
sprohaskagitster
authored andcommitted
xread, xwrite: limit size of IO to 8MB
Checking out 2GB or more through an external filter (see test) fails on Mac OS X 10.8.4 (12E55) for a 64-bit executable with: error: read from external filter cat failed error: cannot feed the input to external filter cat error: cat died of signal 13 error: external filter cat failed 141 error: external filter cat failed The reason is that read() immediately returns with EINVAL when asked to read more than 2GB. According to POSIX [1], if the value of nbyte passed to read() is greater than SSIZE_MAX, the result is implementation-defined. The write function has the same restriction [2]. Since OS X still supports running 32-bit executables, the 32-bit limit (SSIZE_MAX = INT_MAX = 2GB - 1) seems to be also imposed on 64-bit executables under certain conditions. For write, the problem has been addressed earlier [6c642a]. Address the problem for read() and write() differently, by limiting size of IO chunks unconditionally on all platforms in xread() and xwrite(). Large chunks only cause problems, like causing latencies when killing the process, even if OS X was not buggy. Doing IO in reasonably sized smaller chunks should have no negative impact on performance. The compat wrapper clipped_write() introduced earlier [6c642a] is not needed anymore. It will be reverted in a separate commit. The new test catches read and write problems. Note that 'git add' exits with 0 even if it prints filtering errors to stderr. The test, therefore, checks stderr. 'git add' should probably be changed (sometime in another commit) to exit with nonzero if filtering fails. The test could then be changed to use test_must_fail. Thanks to the following people for suggestions and testing: Johannes Sixt <[email protected]> John Keeping <[email protected]> Jonathan Nieder <[email protected]> Kyle J. McKay <[email protected]> Linus Torvalds <[email protected]> Torsten Bögershausen <[email protected]> [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html [2] http://pubs.opengroup.org/onlinepubs/009695399/functions/write.html [6c642a] commit 6c642a8 compate/clipped-write.c: large write(2) fails on Mac OS X/XNU Signed-off-by: Steffen Prohaska <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4d06473 commit 0b6806b

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

t/t0021-conversion.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,18 @@ test_expect_success 'required filter clean failure' '
190190
test_must_fail git add test.fc
191191
'
192192

193+
test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
194+
195+
test_expect_success EXPENSIVE 'filter large file' '
196+
git config filter.largefile.smudge cat &&
197+
git config filter.largefile.clean cat &&
198+
for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
199+
echo "2GB filter=largefile" >.gitattributes &&
200+
git add 2GB 2>err &&
201+
! test -s err &&
202+
rm -f 2GB &&
203+
git checkout -- 2GB 2>err &&
204+
! test -s err
205+
'
206+
193207
test_done

wrapper.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ void *xcalloc(size_t nmemb, size_t size)
130130
return ret;
131131
}
132132

133+
/*
134+
* Limit size of IO chunks, because huge chunks only cause pain. OS X
135+
* 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
136+
* the absense of bugs, large chunks can result in bad latencies when
137+
* you decide to kill the process.
138+
*/
139+
#define MAX_IO_SIZE (8*1024*1024)
140+
133141
/*
134142
* xread() is the same a read(), but it automatically restarts read()
135143
* operations with a recoverable error (EAGAIN and EINTR). xread()
@@ -138,6 +146,8 @@ void *xcalloc(size_t nmemb, size_t size)
138146
ssize_t xread(int fd, void *buf, size_t len)
139147
{
140148
ssize_t nr;
149+
if (len > MAX_IO_SIZE)
150+
len = MAX_IO_SIZE;
141151
while (1) {
142152
nr = read(fd, buf, len);
143153
if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
@@ -154,6 +164,8 @@ ssize_t xread(int fd, void *buf, size_t len)
154164
ssize_t xwrite(int fd, const void *buf, size_t len)
155165
{
156166
ssize_t nr;
167+
if (len > MAX_IO_SIZE)
168+
len = MAX_IO_SIZE;
157169
while (1) {
158170
nr = write(fd, buf, len);
159171
if ((nr < 0) && (errno == EAGAIN || errno == EINTR))

0 commit comments

Comments
 (0)