diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-11-22 08:16:57 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-11-22 08:16:57 +0000 |
commit | 7d75a96b15327050a294b1f54981781ce6e551e2 (patch) | |
tree | df8a1fcb74b6bd7869fe60af461c11f0684df043 | |
parent | 705eaf8b403555741cf6313a76da8597ae54d324 (diff) | |
download | busybox-w32-7d75a96b15327050a294b1f54981781ce6e551e2.tar.gz busybox-w32-7d75a96b15327050a294b1f54981781ce6e551e2.tar.bz2 busybox-w32-7d75a96b15327050a294b1f54981781ce6e551e2.zip |
ash: fix bug where redirection of closed fd was leaving it open afterwards.
redirect 983 1024 +41
bb_echo 276 301 +25
popredir 118 132 +14
evalcommand 1163 1176 +13
bbunpack 358 366 +8
echocmd 13 5 -8
echo_main 13 5 -8
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/2 up/down: 101/-16) Total: 85 bytes
text data bss dec hex filename
774999 962 9236 785197 bfb2d busybox_old
775084 962 9236 785282 bfb82 busybox_unstripped
-rw-r--r-- | coreutils/echo.c | 141 | ||||
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | shell/ash.c | 35 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir.right | 1 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir.tests | 6 |
5 files changed, 169 insertions, 16 deletions
diff --git a/coreutils/echo.c b/coreutils/echo.c index 860853f02..4c7a91743 100644 --- a/coreutils/echo.c +++ b/coreutils/echo.c | |||
@@ -25,7 +25,9 @@ | |||
25 | 25 | ||
26 | #include "libbb.h" | 26 | #include "libbb.h" |
27 | 27 | ||
28 | int bb_echo(char **argv) | 28 | /* argc is unused, but removing it precludes compiler from |
29 | * using call -> jump optimization */ | ||
30 | int bb_echo(int argc, char **argv) | ||
29 | { | 31 | { |
30 | const char *arg; | 32 | const char *arg; |
31 | #if !ENABLE_FEATURE_FANCY_ECHO | 33 | #if !ENABLE_FEATURE_FANCY_ECHO |
@@ -33,6 +35,18 @@ int bb_echo(char **argv) | |||
33 | eflag = '\\', | 35 | eflag = '\\', |
34 | nflag = 1, /* 1 -- print '\n' */ | 36 | nflag = 1, /* 1 -- print '\n' */ |
35 | }; | 37 | }; |
38 | |||
39 | /* We must check that stdout is not closed. | ||
40 | * The reason for this is highly non-obvious. | ||
41 | * bb_echo is used from shell. Shell must correctly handle "echo foo" | ||
42 | * if stdout is closed. With stdio, output gets shoveled into | ||
43 | * stdout buffer, and even fflush cannot clear it out. It seems that | ||
44 | * even if libc receives EBADF on write attempts, it feels determined | ||
45 | * to output data no matter what. So it will try later, | ||
46 | * and possibly will clobber future output. Not good. */ | ||
47 | if (dup2(1, 1) != 1) | ||
48 | return -1; | ||
49 | |||
36 | arg = *++argv; | 50 | arg = *++argv; |
37 | if (!arg) | 51 | if (!arg) |
38 | goto newline_ret; | 52 | goto newline_ret; |
@@ -41,6 +55,10 @@ int bb_echo(char **argv) | |||
41 | char nflag = 1; | 55 | char nflag = 1; |
42 | char eflag = 0; | 56 | char eflag = 0; |
43 | 57 | ||
58 | /* We must check that stdout is not closed. */ | ||
59 | if (dup2(1, 1) != 1) | ||
60 | return -1; | ||
61 | |||
44 | while (1) { | 62 | while (1) { |
45 | arg = *++argv; | 63 | arg = *++argv; |
46 | if (!arg) | 64 | if (!arg) |
@@ -122,7 +140,7 @@ int bb_echo(char **argv) | |||
122 | int echo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 140 | int echo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
123 | int echo_main(int argc, char **argv) | 141 | int echo_main(int argc, char **argv) |
124 | { | 142 | { |
125 | return bb_echo(argv); | 143 | return bb_echo(argc, argv); |
126 | } | 144 | } |
127 | 145 | ||
128 | /*- | 146 | /*- |
@@ -163,3 +181,122 @@ int echo_main(int argc, char **argv) | |||
163 | * | 181 | * |
164 | * @(#)echo.c 8.1 (Berkeley) 5/31/93 | 182 | * @(#)echo.c 8.1 (Berkeley) 5/31/93 |
165 | */ | 183 | */ |
184 | |||
185 | #ifdef VERSION_WITH_WRITEV | ||
186 | /* We can't use stdio. | ||
187 | * The reason for this is highly non-obvious. | ||
188 | * bb_echo is used from shell. Shell must correctly handle "echo foo" | ||
189 | * if stdout is closed. With stdio, output gets shoveled into | ||
190 | * stdout buffer, and even fflush cannot clear it out. It seems that | ||
191 | * even if libc receives EBADF on write attempts, it feels determined | ||
192 | * to output data no matter what. So it will try later, | ||
193 | * and possibly will clobber future output. Not good. | ||
194 | * | ||
195 | * Using writev instead, with 'direct' conversion of argv vector. | ||
196 | */ | ||
197 | |||
198 | int bb_echo(int argc, char **argv) | ||
199 | { | ||
200 | struct iovec io[argc]; | ||
201 | struct iovec *cur_io = io; | ||
202 | char *arg; | ||
203 | char *p; | ||
204 | #if !ENABLE_FEATURE_FANCY_ECHO | ||
205 | enum { | ||
206 | eflag = '\\', | ||
207 | nflag = 1, /* 1 -- print '\n' */ | ||
208 | }; | ||
209 | arg = *++argv; | ||
210 | if (!arg) | ||
211 | goto newline_ret; | ||
212 | #else | ||
213 | char nflag = 1; | ||
214 | char eflag = 0; | ||
215 | |||
216 | while (1) { | ||
217 | arg = *++argv; | ||
218 | if (!arg) | ||
219 | goto newline_ret; | ||
220 | if (*arg != '-') | ||
221 | break; | ||
222 | |||
223 | /* If it appears that we are handling options, then make sure | ||
224 | * that all of the options specified are actually valid. | ||
225 | * Otherwise, the string should just be echoed. | ||
226 | */ | ||
227 | p = arg + 1; | ||
228 | if (!*p) /* A single '-', so echo it. */ | ||
229 | goto just_echo; | ||
230 | |||
231 | do { | ||
232 | if (!strrchr("neE", *p)) | ||
233 | goto just_echo; | ||
234 | } while (*++p); | ||
235 | |||
236 | /* All of the options in this arg are valid, so handle them. */ | ||
237 | p = arg + 1; | ||
238 | do { | ||
239 | if (*p == 'n') | ||
240 | nflag = 0; | ||
241 | if (*p == 'e') | ||
242 | eflag = '\\'; | ||
243 | } while (*++p); | ||
244 | } | ||
245 | just_echo: | ||
246 | #endif | ||
247 | |||
248 | while (1) { | ||
249 | /* arg is already == *argv and isn't NULL */ | ||
250 | int c; | ||
251 | |||
252 | cur_io->iov_base = p = arg; | ||
253 | |||
254 | if (!eflag) { | ||
255 | /* optimization for very common case */ | ||
256 | p += strlen(arg); | ||
257 | } else while ((c = *arg++)) { | ||
258 | if (c == eflag) { /* Check for escape seq. */ | ||
259 | if (*arg == 'c') { | ||
260 | /* '\c' means cancel newline and | ||
261 | * ignore all subsequent chars. */ | ||
262 | cur_io->iov_len = p - (char*)cur_io->iov_base; | ||
263 | cur_io++; | ||
264 | goto ret; | ||
265 | } | ||
266 | #if !ENABLE_FEATURE_FANCY_ECHO | ||
267 | /* SUSv3 specifies that octal escapes must begin with '0'. */ | ||
268 | if ( (((unsigned char)*arg) - '1') >= 7) | ||
269 | #endif | ||
270 | { | ||
271 | /* Since SUSv3 mandates a first digit of 0, 4-digit octals | ||
272 | * of the form \0### are accepted. */ | ||
273 | if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) { | ||
274 | arg++; | ||
275 | } | ||
276 | /* bb_process_escape_sequence can handle nul correctly */ | ||
277 | c = bb_process_escape_sequence( (void*) &arg); | ||
278 | } | ||
279 | } | ||
280 | *p++ = c; | ||
281 | } | ||
282 | |||
283 | arg = *++argv; | ||
284 | if (arg) | ||
285 | *p++ = ' '; | ||
286 | cur_io->iov_len = p - (char*)cur_io->iov_base; | ||
287 | cur_io++; | ||
288 | if (!arg) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | newline_ret: | ||
293 | if (nflag) { | ||
294 | cur_io->iov_base = (char*)"\n"; | ||
295 | cur_io->iov_len = 1; | ||
296 | cur_io++; | ||
297 | } | ||
298 | ret: | ||
299 | /* TODO: implement and use full_writev? */ | ||
300 | return writev(1, io, (cur_io - io)) >= 0; | ||
301 | } | ||
302 | #endif | ||
diff --git a/include/libbb.h b/include/libbb.h index 3bec43233..77392d09c 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -728,7 +728,7 @@ extern void bb_verror_msg(const char *s, va_list p, const char *strerr); | |||
728 | 728 | ||
729 | /* applets which are useful from another applets */ | 729 | /* applets which are useful from another applets */ |
730 | int bb_cat(char** argv); | 730 | int bb_cat(char** argv); |
731 | int bb_echo(char** argv); | 731 | int bb_echo(int argc, char** argv); |
732 | int test_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 732 | int test_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
733 | int kill_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 733 | int kill_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
734 | #if ENABLE_ROUTE | 734 | #if ENABLE_ROUTE |
diff --git a/shell/ash.c b/shell/ash.c index bb930f55f..4113ce8e2 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -4567,6 +4567,7 @@ stoppedjobs(void) | |||
4567 | */ | 4567 | */ |
4568 | 4568 | ||
4569 | #define EMPTY -2 /* marks an unused slot in redirtab */ | 4569 | #define EMPTY -2 /* marks an unused slot in redirtab */ |
4570 | #define CLOSED -3 /* marks a slot of previously-closed fd */ | ||
4570 | #ifndef PIPE_BUF | 4571 | #ifndef PIPE_BUF |
4571 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ | 4572 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ |
4572 | #else | 4573 | #else |
@@ -4791,7 +4792,7 @@ redirect(union node *redir, int flags) | |||
4791 | int i; | 4792 | int i; |
4792 | int fd; | 4793 | int fd; |
4793 | int newfd; | 4794 | int newfd; |
4794 | int *p; | 4795 | |
4795 | nullredirs++; | 4796 | nullredirs++; |
4796 | if (!redir) { | 4797 | if (!redir) { |
4797 | return; | 4798 | return; |
@@ -4799,15 +4800,13 @@ redirect(union node *redir, int flags) | |||
4799 | sv = NULL; | 4800 | sv = NULL; |
4800 | INT_OFF; | 4801 | INT_OFF; |
4801 | if (flags & REDIR_PUSH) { | 4802 | if (flags & REDIR_PUSH) { |
4802 | struct redirtab *q; | 4803 | sv = ckmalloc(sizeof(*sv)); |
4803 | q = ckmalloc(sizeof(struct redirtab)); | 4804 | sv->next = redirlist; |
4804 | q->next = redirlist; | 4805 | redirlist = sv; |
4805 | redirlist = q; | 4806 | sv->nullredirs = nullredirs - 1; |
4806 | q->nullredirs = nullredirs - 1; | ||
4807 | for (i = 0; i < 10; i++) | 4807 | for (i = 0; i < 10; i++) |
4808 | q->renamed[i] = EMPTY; | 4808 | sv->renamed[i] = EMPTY; |
4809 | nullredirs = 0; | 4809 | nullredirs = 0; |
4810 | sv = q; | ||
4811 | } | 4810 | } |
4812 | n = redir; | 4811 | n = redir; |
4813 | do { | 4812 | do { |
@@ -4817,9 +4816,14 @@ redirect(union node *redir, int flags) | |||
4817 | continue; /* redirect from/to same file descriptor */ | 4816 | continue; /* redirect from/to same file descriptor */ |
4818 | 4817 | ||
4819 | newfd = openredirect(n); | 4818 | newfd = openredirect(n); |
4820 | if (fd == newfd) | 4819 | if (fd == newfd) { |
4820 | /* Descriptor wasn't open before redirect. | ||
4821 | * Mark it for close in the future */ | ||
4822 | if (sv && sv->renamed[fd] == EMPTY) | ||
4823 | sv->renamed[fd] = CLOSED; | ||
4821 | continue; | 4824 | continue; |
4822 | if (sv && *(p = &sv->renamed[fd]) == EMPTY) { | 4825 | } |
4826 | if (sv && sv->renamed[fd] == EMPTY) { | ||
4823 | i = fcntl(fd, F_DUPFD, 10); | 4827 | i = fcntl(fd, F_DUPFD, 10); |
4824 | 4828 | ||
4825 | if (i == -1) { | 4829 | if (i == -1) { |
@@ -4831,7 +4835,7 @@ redirect(union node *redir, int flags) | |||
4831 | /* NOTREACHED */ | 4835 | /* NOTREACHED */ |
4832 | } | 4836 | } |
4833 | } else { | 4837 | } else { |
4834 | *p = i; | 4838 | sv->renamed[fd] = i; |
4835 | close(fd); | 4839 | close(fd); |
4836 | } | 4840 | } |
4837 | } else { | 4841 | } else { |
@@ -4840,7 +4844,7 @@ redirect(union node *redir, int flags) | |||
4840 | dupredirect(n, newfd); | 4844 | dupredirect(n, newfd); |
4841 | } while ((n = n->nfile.next)); | 4845 | } while ((n = n->nfile.next)); |
4842 | INT_ON; | 4846 | INT_ON; |
4843 | if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0) | 4847 | if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0) |
4844 | preverrout_fd = sv->renamed[2]; | 4848 | preverrout_fd = sv->renamed[2]; |
4845 | } | 4849 | } |
4846 | 4850 | ||
@@ -4858,6 +4862,11 @@ popredir(int drop) | |||
4858 | INT_OFF; | 4862 | INT_OFF; |
4859 | rp = redirlist; | 4863 | rp = redirlist; |
4860 | for (i = 0; i < 10; i++) { | 4864 | for (i = 0; i < 10; i++) { |
4865 | if (rp->renamed[i] == CLOSED) { | ||
4866 | if (!drop) | ||
4867 | close(i); | ||
4868 | continue; | ||
4869 | } | ||
4861 | if (rp->renamed[i] != EMPTY) { | 4870 | if (rp->renamed[i] != EMPTY) { |
4862 | if (!drop) { | 4871 | if (!drop) { |
4863 | close(i); | 4872 | close(i); |
@@ -10994,7 +11003,7 @@ exitcmd(int argc, char **argv) | |||
10994 | static int | 11003 | static int |
10995 | echocmd(int argc, char **argv) | 11004 | echocmd(int argc, char **argv) |
10996 | { | 11005 | { |
10997 | return bb_echo(argv); | 11006 | return bb_echo(argc, argv); |
10998 | } | 11007 | } |
10999 | #endif | 11008 | #endif |
11000 | 11009 | ||
diff --git a/shell/ash_test/ash-redir/redir.right b/shell/ash_test/ash-redir/redir.right new file mode 100644 index 000000000..2a02d41ce --- /dev/null +++ b/shell/ash_test/ash-redir/redir.right | |||
@@ -0,0 +1 @@ | |||
TEST | |||
diff --git a/shell/ash_test/ash-redir/redir.tests b/shell/ash_test/ash-redir/redir.tests new file mode 100644 index 000000000..7a1a66806 --- /dev/null +++ b/shell/ash_test/ash-redir/redir.tests | |||
@@ -0,0 +1,6 @@ | |||
1 | # test: closed fds should stay closed | ||
2 | exec 1>&- | ||
3 | echo TEST >TEST | ||
4 | echo JUNK # lost: stdout is closed | ||
5 | cat TEST >&2 | ||
6 | rm TEST | ||