summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2017-03-20 05:36:23 (GMT)
committerPádraig Brady <P@draigBrady.com>2017-03-26 22:20:23 (GMT)
commita79dbb97bfb2fff7905221f2437da19472b9ba23 (patch)
tree0e229c376025b9ff7e271eb01b68de7e60edf49f
parent04148c99c514d9adea1e3cd831d5451f957f1074 (diff)
downloadcoreutils-a79dbb97bfb2fff7905221f2437da19472b9ba23.zip
coreutils-a79dbb97bfb2fff7905221f2437da19472b9ba23.tar.gz
coreutils-a79dbb97bfb2fff7905221f2437da19472b9ba23.tar.bz2
split: ensure input is processed when filters exit early
commit v8.25-4-g62e7af0 introduced the issue as it broke out of the processing loop irrespective of the value of new_file_flag which was used to indicate a finite number of filters or not. For example, this ran forever (as it should): $ yes | split --filter="head -c1 >/dev/null" -b 1000 However this exited immediately due to EPIPE being propagated back through cwrite and the loop not considering new filters: $ yes | split --filter="head -c1 >/dev/null" -b 100000 Similarly processing would exit early for a bounded number of output files, resulting in empty data sent to all but the first: $ truncate -s10T big.in $ split --filter='head -c1 >$FILE' -n 2 big.in $ echo $(stat -c%s x??) 1 0 I was alerted to this code by clang-analyzer, which indicated dead assigments, which is often an indication of code that hasn't considered all cases. * src/split.c (bytes_split): Change the last condition in the processing loop to also consider the number of files before breaking out of the processing loop. * tests/split/filter.sh: Add a test case. * NEWS: Mention the bug fix.
-rw-r--r--NEWS5
-rw-r--r--src/split.c3
-rwxr-xr-xtests/split/filter.sh6
3 files changed, 13 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index 409dbe0..e8d6d34 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,11 @@ GNU coreutils NEWS -*- outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** Bug fixes
+
+ split no longer exits when invocations of a --filter return EPIPE.
+ [bug introduced in coreutils-8.26]
+
* Noteworthy changes in release 8.27 (2017-03-08) [stable]
diff --git a/src/split.c b/src/split.c
index 9662336..85bc052 100644
--- a/src/split.c
+++ b/src/split.c
@@ -654,6 +654,7 @@ bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize, size_t initial_read,
{
/* If filter no longer accepting input, stop reading. */
n_read = to_read = 0;
+ eof = true;
break;
}
bp_out += w;
@@ -666,7 +667,7 @@ bytes_split (uintmax_t n_bytes, char *buf, size_t bufsize, size_t initial_read,
opened += new_file_flag;
to_write -= to_read;
new_file_flag = false;
- if (!cwrite_ok)
+ if (!cwrite_ok && opened == max_files)
{
/* If filter no longer accepting input, stop reading. */
n_read = 0;
diff --git a/tests/split/filter.sh b/tests/split/filter.sh
index 8d211c0..a85093c 100755
--- a/tests/split/filter.sh
+++ b/tests/split/filter.sh
@@ -62,4 +62,10 @@ if truncate -s$N zero.in; then
timeout 10 sh -c 'split --filter="head -c1 >/dev/null" -n 1 zero.in' || fail=1
fi
+# Ensure that "endless" input _is_ processed for unbounded number of filters
+for buf in 1000 1000000; do
+ returns_ 124 timeout .5 sh -c \
+ "yes | split --filter='head -c1 >/dev/null' -b $buf" || fail=1
+done
+
Exit $fail