diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-24 19:46:38 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-24 19:46:38 +0000 |
commit | 5a867317bb1cbf36d396d9cdb552212607dcc2b1 (patch) | |
tree | e8c01f8c4f8242694db730eed60a3616e86f3a48 | |
parent | 6fbb43bc3c14fc30030cae77db51c322bece30ab (diff) | |
download | busybox-w32-5a867317bb1cbf36d396d9cdb552212607dcc2b1.tar.gz busybox-w32-5a867317bb1cbf36d396d9cdb552212607dcc2b1.tar.bz2 busybox-w32-5a867317bb1cbf36d396d9cdb552212607dcc2b1.zip |
ash: fix a bug where redirection fds were not closed afterwards.
optimize close+fcntl(DUPFD) into dup2. add testsuites.
function old new delta
copyfd 47 68 +21
argstr 1311 1298 -13
popredir 148 131 -17
redirect 1139 1107 -32
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/3 up/down: 21/-62) Total: -41 bytes
-rw-r--r-- | shell/ash.c | 74 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir2.right | 1 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir2.tests | 5 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir3.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir3.tests | 5 | ||||
-rwxr-xr-x | shell/ash_test/run-all | 4 |
6 files changed, 53 insertions, 39 deletions
diff --git a/shell/ash.c b/shell/ash.c index 22575ffbc..ec3bd0927 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -4846,12 +4846,21 @@ openredirect(union node *redir) | |||
4846 | * if the source file descriptor is closed, EMPTY if there are no unused | 4846 | * if the source file descriptor is closed, EMPTY if there are no unused |
4847 | * file descriptors left. | 4847 | * file descriptors left. |
4848 | */ | 4848 | */ |
4849 | /* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD). | ||
4850 | * old code was doing close(to) prior to copyfd() to achieve the same */ | ||
4851 | #define COPYFD_EXACT ((int)~INT_MAX) | ||
4849 | static int | 4852 | static int |
4850 | copyfd(int from, int to) | 4853 | copyfd(int from, int to) |
4851 | { | 4854 | { |
4852 | int newfd; | 4855 | int newfd; |
4853 | 4856 | ||
4854 | newfd = fcntl(from, F_DUPFD, to); | 4857 | if (to & COPYFD_EXACT) { |
4858 | to &= ~COPYFD_EXACT; | ||
4859 | /*if (from != to)*/ | ||
4860 | newfd = dup2(from, to); | ||
4861 | } else { | ||
4862 | newfd = fcntl(from, F_DUPFD, to); | ||
4863 | } | ||
4855 | if (newfd < 0) { | 4864 | if (newfd < 0) { |
4856 | if (errno == EMFILE) | 4865 | if (errno == EMFILE) |
4857 | return EMPTY; | 4866 | return EMPTY; |
@@ -4947,11 +4956,7 @@ redirect(union node *redir, int flags) | |||
4947 | /* Descriptor wasn't open before redirect. | 4956 | /* Descriptor wasn't open before redirect. |
4948 | * Mark it for close in the future */ | 4957 | * Mark it for close in the future */ |
4949 | if (need_to_remember(sv, fd)) { | 4958 | if (need_to_remember(sv, fd)) { |
4950 | if (fd == 2) | 4959 | goto remember_to_close; |
4951 | copied_fd2 = CLOSED; /// do we need this? | ||
4952 | sv->two_fd[sv_pos].orig = fd; | ||
4953 | sv->two_fd[sv_pos].copy = CLOSED; | ||
4954 | sv_pos++; | ||
4955 | } | 4960 | } |
4956 | continue; | 4961 | continue; |
4957 | } | 4962 | } |
@@ -4959,6 +4964,10 @@ redirect(union node *redir, int flags) | |||
4959 | if (need_to_remember(sv, fd)) { | 4964 | if (need_to_remember(sv, fd)) { |
4960 | /* Copy old descriptor */ | 4965 | /* Copy old descriptor */ |
4961 | i = fcntl(fd, F_DUPFD, 10); | 4966 | i = fcntl(fd, F_DUPFD, 10); |
4967 | /* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds | ||
4968 | * are closed in popredir() in the child, preventing them from leaking | ||
4969 | * into child. (popredir() also cleans up the mess in case of failures) | ||
4970 | */ | ||
4962 | if (i == -1) { | 4971 | if (i == -1) { |
4963 | i = errno; | 4972 | i = errno; |
4964 | if (i != EBADF) { | 4973 | if (i != EBADF) { |
@@ -4969,31 +4978,25 @@ redirect(union node *redir, int flags) | |||
4969 | ash_msg_and_raise_error("%d: %m", fd); | 4978 | ash_msg_and_raise_error("%d: %m", fd); |
4970 | /* NOTREACHED */ | 4979 | /* NOTREACHED */ |
4971 | } | 4980 | } |
4972 | /* EBADF: it is not open - ok */ | 4981 | /* EBADF: it is not open - good, remember to close it */ |
4973 | } else { | 4982 | remember_to_close: |
4974 | /* fd is open, save its copy */ | 4983 | i = CLOSED; |
4975 | /* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds | 4984 | } /* else: fd is open, save its copy */ |
4976 | * are closed in popredir() in the child, preventing them from leaking | 4985 | if (fd == 2) |
4977 | * into child. (popredir() also cleans up the mess in case of failures) | 4986 | copied_fd2 = i; |
4978 | */ | 4987 | sv->two_fd[sv_pos].orig = fd; |
4979 | if (fd == 2) | 4988 | sv->two_fd[sv_pos].copy = i; |
4980 | copied_fd2 = i; | 4989 | sv_pos++; |
4981 | sv->two_fd[sv_pos].orig = fd; | ||
4982 | sv->two_fd[sv_pos].copy = i; | ||
4983 | sv_pos++; | ||
4984 | close(fd); | ||
4985 | } | ||
4986 | } else { | ||
4987 | close(fd); | ||
4988 | } | 4990 | } |
4989 | /* At this point fd is closed */ | ||
4990 | if (newfd < 0) { | 4991 | if (newfd < 0) { |
4991 | /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ | 4992 | /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ |
4992 | if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ | 4993 | if (redir->ndup.dupfd < 0) { /* "NN>&-" */ |
4993 | copyfd(redir->ndup.dupfd, fd); | 4994 | close(fd); |
4995 | } else { | ||
4996 | copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); | ||
4994 | } | 4997 | } |
4995 | } else { /* move newfd to fd */ | 4998 | } else if (fd != newfd) { /* move newfd to fd */ |
4996 | copyfd(newfd, fd); | 4999 | copyfd(newfd, fd | COPYFD_EXACT); |
4997 | close(newfd); | 5000 | close(newfd); |
4998 | } | 5001 | } |
4999 | } while ((redir = redir->nfile.next) != NULL); | 5002 | } while ((redir = redir->nfile.next) != NULL); |
@@ -5025,8 +5028,8 @@ popredir(int drop) | |||
5025 | } | 5028 | } |
5026 | if (rp->two_fd[i].copy != EMPTY) { | 5029 | if (rp->two_fd[i].copy != EMPTY) { |
5027 | if (!drop) { | 5030 | if (!drop) { |
5028 | close(fd); | 5031 | /*close(fd);*/ |
5029 | copyfd(rp->two_fd[i].copy, fd); | 5032 | copyfd(rp->two_fd[i].copy, fd | COPYFD_EXACT); |
5030 | } | 5033 | } |
5031 | close(rp->two_fd[i].copy); | 5034 | close(rp->two_fd[i].copy); |
5032 | } | 5035 | } |
@@ -5064,7 +5067,8 @@ redirectsafe(union node *redir, int flags) | |||
5064 | struct jmploc jmploc; | 5067 | struct jmploc jmploc; |
5065 | 5068 | ||
5066 | SAVE_INT(saveint); | 5069 | SAVE_INT(saveint); |
5067 | err = setjmp(jmploc.loc) * 2; | 5070 | /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ |
5071 | err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2; | ||
5068 | if (!err) { | 5072 | if (!err) { |
5069 | exception_handler = &jmploc; | 5073 | exception_handler = &jmploc; |
5070 | redirect(redir, flags); | 5074 | redirect(redir, flags); |
@@ -5443,8 +5447,8 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
5443 | FORCE_INT_ON; | 5447 | FORCE_INT_ON; |
5444 | close(pip[0]); | 5448 | close(pip[0]); |
5445 | if (pip[1] != 1) { | 5449 | if (pip[1] != 1) { |
5446 | close(1); | 5450 | /*close(1);*/ |
5447 | copyfd(pip[1], 1); | 5451 | copyfd(pip[1], 1 | COPYFD_EXACT); |
5448 | close(pip[1]); | 5452 | close(pip[1]); |
5449 | } | 5453 | } |
5450 | eflag = 0; | 5454 | eflag = 0; |
@@ -10691,11 +10695,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) | |||
10691 | len = out - (char *)stackblock(); | 10695 | len = out - (char *)stackblock(); |
10692 | out = stackblock(); | 10696 | out = stackblock(); |
10693 | if (eofmark == NULL) { | 10697 | if (eofmark == NULL) { |
10694 | if ((c == '>' || c == '<') | 10698 | if ((c == '>' || c == '<') && quotef == 0) { |
10695 | && quotef == 0 | ||
10696 | // && len <= 2 // THIS LIMITS fd to 1 char: N>file, but no NN>file! | ||
10697 | // && (*out == '\0' || isdigit(*out)) | ||
10698 | ) { | ||
10699 | int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */ | 10699 | int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */ |
10700 | char *np = out; | 10700 | char *np = out; |
10701 | while (--maxlen && isdigit(*np)) | 10701 | while (--maxlen && isdigit(*np)) |
diff --git a/shell/ash_test/ash-redir/redir2.right b/shell/ash_test/ash-redir/redir2.right new file mode 100644 index 000000000..d86bac9de --- /dev/null +++ b/shell/ash_test/ash-redir/redir2.right | |||
@@ -0,0 +1 @@ | |||
OK | |||
diff --git a/shell/ash_test/ash-redir/redir2.tests b/shell/ash_test/ash-redir/redir2.tests new file mode 100755 index 000000000..61ccea30c --- /dev/null +++ b/shell/ash_test/ash-redir/redir2.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | # ash once couldn't redirect above fd#9 | ||
2 | exec 1>/dev/null | ||
3 | (echo LOST1 >&22) 22>&1 | ||
4 | (echo LOST2 >&22) 22>&1 | ||
5 | (echo OK >&22) 22>&2 | ||
diff --git a/shell/ash_test/ash-redir/redir3.right b/shell/ash_test/ash-redir/redir3.right new file mode 100644 index 000000000..fd641a8ea --- /dev/null +++ b/shell/ash_test/ash-redir/redir3.right | |||
@@ -0,0 +1,3 @@ | |||
1 | TEST | ||
2 | ./redir3.tests: line 4: 9: Bad file descriptor | ||
3 | Output to fd#9: 1 | ||
diff --git a/shell/ash_test/ash-redir/redir3.tests b/shell/ash_test/ash-redir/redir3.tests new file mode 100755 index 000000000..f50a7674c --- /dev/null +++ b/shell/ash_test/ash-redir/redir3.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | # redirects to closed descriptors should not leave these descriptors" | ||
2 | # open afterwards | ||
3 | echo TEST 9>/dev/null | ||
4 | echo MUST ERROR OUT >&9 | ||
5 | echo "Output to fd#9: $?" | ||
diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all index b0a6d1088..4d0f39a41 100755 --- a/shell/ash_test/run-all +++ b/shell/ash_test/run-all | |||
@@ -3,8 +3,8 @@ | |||
3 | TOPDIR=$PWD | 3 | TOPDIR=$PWD |
4 | 4 | ||
5 | test -x ash || { | 5 | test -x ash || { |
6 | echo "No ./ash?! Perhaps you want to run 'ln -s ../../busybox ash'" | 6 | echo "No ./ash - creating a link to ../../busybox" |
7 | exit | 7 | ln -s ../../busybox ash |
8 | } | 8 | } |
9 | test -x printenv || gcc -O2 -o printenv printenv.c || exit $? | 9 | test -x printenv || gcc -O2 -o printenv printenv.c || exit $? |
10 | test -x recho || gcc -O2 -o recho recho.c || exit $? | 10 | test -x recho || gcc -O2 -o recho recho.c || exit $? |