diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2024-07-12 21:58:04 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2024-07-12 22:10:50 +0200 |
commit | 0829fce079dae45737330114c27886cb8af85043 (patch) | |
tree | 4c15302da692098b3cfa13c045b7818377c99c10 | |
parent | 1ad2f5cd9fe5de0f19212924e100c6d87229c950 (diff) | |
download | busybox-w32-0829fce079dae45737330114c27886cb8af85043.tar.gz busybox-w32-0829fce079dae45737330114c27886cb8af85043.tar.bz2 busybox-w32-0829fce079dae45737330114c27886cb8af85043.zip |
ash: do not abort interactive mode on >&9999 redirect
With very large fd#, the error code path is different
from one for closed but small fd#.
Make it not abort if we are interactive:
$ echo text >&99 # this wasn't buggy
ash: dup2(9,1): Bad file descriptor
$ echo text >&9999 # this was
ash: fcntl(1,F_DUPFD,10000): Invalid argument
function old new delta
.rodata 105637 105661 +24
dup2_or_raise 35 38 +3
redirect 1084 1044 -40
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 27/-40) Total: -13 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash.c | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/shell/ash.c b/shell/ash.c index 094a87390..39455c7c5 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -5621,10 +5621,13 @@ dup2_or_raise(int from, int to) | |||
5621 | newfd = (from != to) ? dup2(from, to) : to; | 5621 | newfd = (from != to) ? dup2(from, to) : to; |
5622 | if (newfd < 0) { | 5622 | if (newfd < 0) { |
5623 | /* Happens when source fd is not open: try "echo >&99" */ | 5623 | /* Happens when source fd is not open: try "echo >&99" */ |
5624 | ash_msg_and_raise_perror("%d", from); | 5624 | ash_msg_and_raise_perror("dup2(%d,%d)", from, to); |
5625 | } | 5625 | } |
5626 | return newfd; | 5626 | return newfd; |
5627 | } | 5627 | } |
5628 | /* The only possible error return is EBADF (fd wasn't open). | ||
5629 | * Transient errors retry, other errors raise exception. | ||
5630 | */ | ||
5628 | static int | 5631 | static int |
5629 | dup_CLOEXEC(int fd, int avoid_fd) | 5632 | dup_CLOEXEC(int fd, int avoid_fd) |
5630 | { | 5633 | { |
@@ -5639,6 +5642,16 @@ dup_CLOEXEC(int fd, int avoid_fd) | |||
5639 | goto repeat; | 5642 | goto repeat; |
5640 | if (errno == EINTR) | 5643 | if (errno == EINTR) |
5641 | goto repeat; | 5644 | goto repeat; |
5645 | if (errno != EBADF) { | ||
5646 | /* "echo >&9999" gets EINVAL trying to save fd 1 to above 9999. | ||
5647 | * We could try saving it _below_ 9999 instead (how?), but | ||
5648 | * this probably means that dup2(9999,1) to effectuate >&9999 | ||
5649 | * would also not work: fd 9999 can't exist. Gracefully bail out. | ||
5650 | * (This differs from "echo >&99" where saving works, but | ||
5651 | * subsequent dup2(99,1) fails if fd 99 is not open). | ||
5652 | */ | ||
5653 | ash_msg_and_raise_perror("fcntl(%d,F_DUPFD,%d)", fd, avoid_fd + 1); | ||
5654 | } | ||
5642 | } | 5655 | } |
5643 | return newfd; | 5656 | return newfd; |
5644 | } | 5657 | } |
@@ -5754,7 +5767,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq) | |||
5754 | new_fd = dup_CLOEXEC(fd, avoid_fd); | 5767 | new_fd = dup_CLOEXEC(fd, avoid_fd); |
5755 | sq->two_fd[i].moved_to = new_fd; | 5768 | sq->two_fd[i].moved_to = new_fd; |
5756 | TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); | 5769 | TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); |
5757 | if (new_fd < 0) /* what? */ | 5770 | if (new_fd < 0) /* EBADF? what? */ |
5758 | xfunc_die(); | 5771 | xfunc_die(); |
5759 | return 0; /* "we did not close fd" */ | 5772 | return 0; /* "we did not close fd" */ |
5760 | } | 5773 | } |
@@ -5769,8 +5782,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq) | |||
5769 | new_fd = dup_CLOEXEC(fd, avoid_fd); | 5782 | new_fd = dup_CLOEXEC(fd, avoid_fd); |
5770 | TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); | 5783 | TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); |
5771 | if (new_fd < 0) { | 5784 | if (new_fd < 0) { |
5772 | if (errno != EBADF) | 5785 | /* EBADF (fd is not open) */ |
5773 | xfunc_die(); | ||
5774 | /* new_fd = CLOSED; - already is -1 */ | 5786 | /* new_fd = CLOSED; - already is -1 */ |
5775 | } | 5787 | } |
5776 | sq->two_fd[i].moved_to = new_fd; | 5788 | sq->two_fd[i].moved_to = new_fd; |