]>
Commit | Line | Data |
---|---|---|
4d44078a AM |
1 | From 443166adaf1c8b91e16a716f3b13f47493b895cc Mon Sep 17 00:00:00 2001 |
2 | From: Bernhard Voelker <mail@bernhard-voelker.de> | |
3 | Date: Tue, 31 May 2016 10:38:52 +0200 | |
4 | Subject: [PATCH] Fix bug #48030: find: -exec + does not pass all arguments in | |
5 | certain cases | |
6 | ||
7 | When the -exec arguments buffer (usually 128k) is full and the given | |
8 | command has been executed with all that arguments, find(1) missed to | |
9 | execute the command yet another time if only 1 another file would have | |
10 | to be processed. | |
11 | Both find(1), i.e., nowadays FTS-version, and oldfind are affected. | |
12 | This bug was present since the implementation of '-exec +' in 2005, | |
13 | see commit FINDUTILS_4_2_11-1-25-gf0a6ac6. | |
14 | ||
15 | * lib/buildcmd.c (bc_push_arg): Move the assignment to set 'state->todo' | |
16 | to 1 down after the immediate execution which resets that flag. | |
17 | * find/testsuite/sv-48030-exec-plus-bug.sh: Add a test. | |
18 | * find/testsuite/Makefile.am (test_shell_progs): Reference the test. | |
19 | * NEWS (Bug Fixes): Mention the fix. | |
20 | ||
21 | Reported by Joe Philip Ninan <indiajoe@gmail.com> in | |
22 | https://savannah.gnu.org/bugs/?48030 | |
23 | ||
24 | Upstream-commit: 8cdc9767e305c9566f537af9d1acf71d1bc6ee8e | |
25 | Signed-off-by: Kamil Dudka <kdudka@redhat.com> | |
26 | --- | |
27 | find/testsuite/Makefile.am | 3 +- | |
28 | find/testsuite/sv-48030-exec-plus-bug.sh | 143 +++++++++++++++++++++++++++++++ | |
29 | lib/buildcmd.c | 10 +-- | |
30 | 3 files changed, 150 insertions(+), 6 deletions(-) | |
31 | create mode 100644 find/testsuite/sv-48030-exec-plus-bug.sh | |
32 | ||
33 | diff --git a/find/testsuite/Makefile.am b/find/testsuite/Makefile.am | |
34 | index c1369c3..ab5dbe8 100644 | |
35 | --- a/find/testsuite/Makefile.am | |
36 | +++ b/find/testsuite/Makefile.am | |
37 | @@ -258,7 +258,8 @@ test_escapechars.sh \ | |
38 | test_escape_c.sh \ | |
39 | test_inode.sh \ | |
40 | sv-34079.sh \ | |
41 | -sv-34976-execdir-fd-leak.sh | |
42 | +sv-34976-execdir-fd-leak.sh \ | |
43 | +sv-48030-exec-plus-bug.sh | |
44 | ||
45 | EXTRA_DIST = $(EXTRA_DIST_EXP) $(EXTRA_DIST_XO) $(EXTRA_DIST_GOLDEN) \ | |
46 | $(test_shell_progs) binary_locations.sh checklists.py | |
47 | diff --git a/find/testsuite/sv-48030-exec-plus-bug.sh b/find/testsuite/sv-48030-exec-plus-bug.sh | |
48 | new file mode 100644 | |
49 | index 0000000..4dbf149 | |
50 | --- /dev/null | |
51 | +++ b/find/testsuite/sv-48030-exec-plus-bug.sh | |
52 | @@ -0,0 +1,143 @@ | |
53 | +#! /bin/sh | |
54 | +# Copyright (C) 2016 Free Software Foundation, Inc. | |
55 | +# | |
56 | +# This program is free software: you can redistribute it and/or modify | |
57 | +# it under the terms of the GNU General Public License as published by | |
58 | +# the Free Software Foundation, either version 3 of the License, or | |
59 | +# (at your option) any later version. | |
60 | +# | |
61 | +# This program is distributed in the hope that it will be useful, | |
62 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
63 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
64 | +# GNU General Public License for more details. | |
65 | +# | |
66 | +# You should have received a copy of the GNU General Public License | |
67 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
68 | +# | |
69 | + | |
70 | +# This test verifies that find invokes the given command for the | |
71 | +# multiple-argument sytax '-exec CMD {} +'. Between FINDUTILS-4.2.12 | |
72 | +# and v4.6.0, find(1) would have failed to execute CMD another time | |
73 | +# if there was only one last single file argument. | |
74 | + | |
75 | +testname="$(basename $0)" | |
76 | + | |
77 | +. "${srcdir}"/binary_locations.sh | |
78 | + | |
79 | +die() { | |
80 | + echo "$@" >&2 | |
81 | + exit 1 | |
82 | +} | |
83 | + | |
84 | +# This is used to simplify checking of the return value | |
85 | +# which is useful when ensuring a command fails as desired. | |
86 | +# I.e., just doing `command ... &&fail=1` will not catch | |
87 | +# a segfault in command for example. With this helper you | |
88 | +# instead check an explicit exit code like | |
89 | +# returns_ 1 command ... || fail | |
90 | +returns_ () { | |
91 | + # Disable tracing so it doesn't interfere with stderr of the wrapped command | |
92 | + { set +x; } 2>/dev/null | |
93 | + | |
94 | + local exp_exit="$1" | |
95 | + shift | |
96 | + "$@" | |
97 | + test $? -eq $exp_exit && ret_=0 || ret_=1 | |
98 | + | |
99 | + set -x | |
100 | + { return $ret_; } 2>/dev/null | |
101 | +} | |
102 | + | |
103 | +# Define the nicest compare available (borrowed from gnulib). | |
104 | +if diff_out_=`exec 2>/dev/null; diff -u "$0" "$0" < /dev/null` \ | |
105 | + && diff -u Makefile "$0" 2>/dev/null | grep '^[+]#!' >/dev/null; then | |
106 | + # diff accepts the -u option and does not (like AIX 7 'diff') produce an | |
107 | + # extra space on column 1 of every content line. | |
108 | + if test -z "$diff_out_"; then | |
109 | + compare () { diff -u "$@"; } | |
110 | + else | |
111 | + compare () | |
112 | + { | |
113 | + if diff -u "$@" > diff.out; then | |
114 | + # No differences were found, but Solaris 'diff' produces output | |
115 | + # "No differences encountered". Hide this output. | |
116 | + rm -f diff.out | |
117 | + true | |
118 | + else | |
119 | + cat diff.out | |
120 | + rm -f diff.out | |
121 | + false | |
122 | + fi | |
123 | + } | |
124 | + fi | |
125 | +elif diff_out_=`exec 2>/dev/null; diff -c "$0" "$0" < /dev/null`; then | |
126 | + if test -z "$diff_out_"; then | |
127 | + compare () { diff -c "$@"; } | |
128 | + else | |
129 | + compare () | |
130 | + { | |
131 | + if diff -c "$@" > diff.out; then | |
132 | + # No differences were found, but AIX and HP-UX 'diff' produce output | |
133 | + # "No differences encountered" or "There are no differences between the | |
134 | + # files.". Hide this output. | |
135 | + rm -f diff.out | |
136 | + true | |
137 | + else | |
138 | + cat diff.out | |
139 | + rm -f diff.out | |
140 | + false | |
141 | + fi | |
142 | + } | |
143 | + fi | |
144 | +elif cmp -s /dev/null /dev/null 2>/dev/null; then | |
145 | + compare () { cmp -s "$@"; } | |
146 | +else | |
147 | + compare () { cmp "$@"; } | |
148 | +fi | |
149 | + | |
150 | +DIR='RashuBug' | |
151 | +# Name of the CMD to execute: the file name must be 6 characters long | |
152 | +# (to trigger the bug in combination with the test files). | |
153 | +CMD='tstcmd' | |
154 | + | |
155 | +# Create test files. | |
156 | +make_test_data() { | |
157 | + # Create the CMD script and check that it works. | |
158 | + mkdir "$DIR" 'bin' \ | |
159 | + && echo 'printf "%s\n" "$@"' > "bin/$CMD" \ | |
160 | + && chmod +x "bin/$CMD" \ | |
161 | + && PATH="$PWD/bin:$PATH" \ | |
162 | + && [ $( "${ftsfind}" bin -maxdepth 0 -exec "$CMD" '{}' + ) = 'bin' ] \ | |
163 | + || return 1 | |
164 | + | |
165 | + # Create expected output file - also used for creating the test data. | |
166 | + { seq -f "${DIR}/abcdefghijklmnopqrstuv%04g" 901 && | |
167 | + seq -f "${DIR}/abcdefghijklmnopqrstu%04g" 902 3719 | |
168 | + } > exp2 \ | |
169 | + && LC_ALL=C sort exp2 > exp \ | |
170 | + && rm exp2 \ | |
171 | + || return 1 | |
172 | + | |
173 | + # Create test files, and check if test data has been created correctly. | |
174 | + xargs touch < exp \ | |
175 | + && [ -f "${DIR}/abcdefghijklmnopqrstu3719" ] \ | |
176 | + && [ 3719 = $( "${ftsfind}" "$DIR" -type f | wc -l ) ] \ | |
177 | + || return 1 | |
178 | +} | |
179 | + | |
180 | +set -x | |
181 | +tmpdir="$(mktemp -d)" \ | |
182 | + && cd "$tmpdir" \ | |
183 | + && make_test_data "${tmpdir}" \ | |
184 | + || die "FAIL: failed to set up the test in ${tmpdir}" | |
185 | + | |
186 | +fail=0 | |
187 | +for exe in "${ftsfind}" "${oldfind}"; do | |
188 | + "$exe" "$DIR" -type f -exec "$CMD" '{}' + > out || fail=1 | |
189 | + LC_ALL=C sort out > out2 || fail=1 | |
190 | + compare exp out2 || fail=1 | |
191 | +done | |
192 | + | |
193 | +cd .. | |
194 | +rm -rf "${tmpdir}" || exit 1 | |
195 | +exit $fail | |
196 | diff --git a/lib/buildcmd.c b/lib/buildcmd.c | |
197 | index a58f67e..27e9ce5 100644 | |
198 | --- a/lib/buildcmd.c | |
199 | +++ b/lib/buildcmd.c | |
200 | @@ -356,11 +356,6 @@ bc_push_arg (struct buildcmd_control *ctl, | |
201 | ||
202 | assert (arg != NULL); | |
203 | ||
204 | - if (!initial_args) | |
205 | - { | |
206 | - state->todo = 1; | |
207 | - } | |
208 | - | |
209 | if (!terminate) | |
210 | { | |
211 | if (state->cmd_argv_chars + len + pfxlen > ctl->arg_max) | |
212 | @@ -380,6 +375,11 @@ bc_push_arg (struct buildcmd_control *ctl, | |
213 | bc_do_exec (ctl, state); | |
214 | } | |
215 | ||
216 | + if (!initial_args) | |
217 | + { | |
218 | + state->todo = 1; | |
219 | + } | |
220 | + | |
221 | if (state->cmd_argc >= state->cmd_argv_alloc) | |
222 | { | |
223 | /* XXX: we could use extendbuf() here. */ | |
224 | -- | |
225 | 2.5.5 | |
226 |