diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-31 04:09:19 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-31 04:21:46 +0200 |
commit | 035486c7500c09616a6c1040d8e70923532a5c2d (patch) | |
tree | 5af08692e5c5376d902d58843d994750eeca25c7 | |
parent | 657e9005a9e31e1da094b260abaa8f335e92301f (diff) | |
download | busybox-w32-035486c7500c09616a6c1040d8e70923532a5c2d.tar.gz busybox-w32-035486c7500c09616a6c1040d8e70923532a5c2d.tar.bz2 busybox-w32-035486c7500c09616a6c1040d8e70923532a5c2d.zip |
ash: significant overhaul of redirect saving logic
New code is similar to what hush is doing.
Make CLOSED to -1: same as dash.
popredir() loses "restore" parameter: same as dash.
COPYFD_RESTORE bit is no longer necessary.
This change fixes this interactive bug:
$ ls -l /proc/$$/fd 10>&-
ash: can't set tty process group: Bad file descriptor
ash: can't set tty process group: Bad file descriptor
[1]+ Done(2) ls -l /proc/${\$}/fd 10>&4294967295
function old new delta
unwindredir 29 27 -2
tryexec 154 152 -2
evaltree 503 501 -2
evalcommand 1369 1367 -2
cmdloop 187 185 -2
redirect 1029 1018 -11
popredir 153 123 -30
need_to_remember 36 - -36
is_hidden_fd 68 - -68
------------------------------------------------------------------------------
(add/remove: 0/2 grow/shrink: 0/7 up/down: 0/-155) Total: -155 bytes
text data bss dec hex filename
914572 485 6848 921905 e1131 busybox_old
914553 485 6848 921886 e111e busybox_unstripped
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/ash.c | 332 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir_script.tests | 9 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir_to_bad_fd255.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir_to_bad_fd255.tests | 3 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir_to_bad_fd3.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir_to_bad_fd3.tests | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-redir/redir_script.tests | 9 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_to_bad_fd255.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-redir/redir_to_bad_fd255.tests | 3 | ||||
-rw-r--r-- | shell/hush_test/hush-redir/redir_to_bad_fd3.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-redir/redir_to_bad_fd3.tests | 3 |
11 files changed, 225 insertions, 143 deletions
diff --git a/shell/ash.c b/shell/ash.c index 1e720aec4..ac676c2dc 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -5192,7 +5192,7 @@ stoppedjobs(void) | |||
5192 | #undef EMPTY | 5192 | #undef EMPTY |
5193 | #undef CLOSED | 5193 | #undef CLOSED |
5194 | #define EMPTY -2 /* marks an unused slot in redirtab */ | 5194 | #define EMPTY -2 /* marks an unused slot in redirtab */ |
5195 | #define CLOSED -3 /* marks a slot of previously-closed fd */ | 5195 | #define CLOSED -1 /* marks a slot of previously-closed fd */ |
5196 | 5196 | ||
5197 | /* | 5197 | /* |
5198 | * Handle here documents. Normally we fork off a process to write the | 5198 | * Handle here documents. Normally we fork off a process to write the |
@@ -5349,74 +5349,158 @@ dup2_or_raise(int from, int to) | |||
5349 | } | 5349 | } |
5350 | return newfd; | 5350 | return newfd; |
5351 | } | 5351 | } |
5352 | static int | ||
5353 | fcntl_F_DUPFD(int fd, int avoid_fd) | ||
5354 | { | ||
5355 | int newfd; | ||
5356 | repeat: | ||
5357 | newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); | ||
5358 | if (newfd < 0) { | ||
5359 | if (errno == EBUSY) | ||
5360 | goto repeat; | ||
5361 | if (errno == EINTR) | ||
5362 | goto repeat; | ||
5363 | } | ||
5364 | return newfd; | ||
5365 | } | ||
5366 | static int | ||
5367 | xdup_CLOEXEC_and_close(int fd, int avoid_fd) | ||
5368 | { | ||
5369 | int newfd; | ||
5370 | repeat: | ||
5371 | newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); | ||
5372 | if (newfd < 0) { | ||
5373 | if (errno == EBUSY) | ||
5374 | goto repeat; | ||
5375 | if (errno == EINTR) | ||
5376 | goto repeat; | ||
5377 | /* fd was not open? */ | ||
5378 | if (errno == EBADF) | ||
5379 | return fd; | ||
5380 | ash_msg_and_raise_perror("%d", newfd); | ||
5381 | } | ||
5382 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
5383 | close(fd); | ||
5384 | return newfd; | ||
5385 | } | ||
5352 | 5386 | ||
5353 | /* Struct def and variable are moved down to the first usage site */ | 5387 | /* Struct def and variable are moved down to the first usage site */ |
5354 | struct two_fd_t { | 5388 | struct squirrel { |
5355 | int orig, copy; | 5389 | int orig_fd; |
5390 | int moved_to; | ||
5356 | }; | 5391 | }; |
5357 | struct redirtab { | 5392 | struct redirtab { |
5358 | struct redirtab *next; | 5393 | struct redirtab *next; |
5359 | int pair_count; | 5394 | int pair_count; |
5360 | struct two_fd_t two_fd[]; | 5395 | struct squirrel two_fd[]; |
5361 | }; | 5396 | }; |
5362 | #define redirlist (G_var.redirlist) | 5397 | #define redirlist (G_var.redirlist) |
5363 | enum { | ||
5364 | COPYFD_RESTORE = (int)~(INT_MAX), | ||
5365 | }; | ||
5366 | 5398 | ||
5367 | static int | 5399 | static void |
5368 | need_to_remember(struct redirtab *rp, int fd) | 5400 | add_squirrel_closed(struct redirtab *sq, int fd) |
5369 | { | 5401 | { |
5370 | int i; | 5402 | int i; |
5371 | 5403 | ||
5372 | if (!rp) /* remembering was not requested */ | 5404 | if (!sq) |
5373 | return 0; | 5405 | return; |
5374 | 5406 | ||
5375 | for (i = 0; i < rp->pair_count; i++) { | 5407 | for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) { |
5376 | if (rp->two_fd[i].orig == fd) { | 5408 | /* If we collide with an already moved fd... */ |
5377 | /* already remembered */ | 5409 | if (fd == sq->two_fd[i].orig_fd) { |
5378 | return 0; | 5410 | /* Examples: |
5411 | * "echo 3>FILE 3>&- 3>FILE" | ||
5412 | * "echo 3>&- 3>FILE" | ||
5413 | * No need for last redirect to insert | ||
5414 | * another "need to close 3" indicator. | ||
5415 | */ | ||
5416 | TRACE(("redirect_fd %d: already moved or closed\n", fd)); | ||
5417 | return; | ||
5379 | } | 5418 | } |
5380 | } | 5419 | } |
5381 | return 1; | 5420 | TRACE(("redirect_fd %d: previous fd was closed\n", fd)); |
5421 | sq->two_fd[i].orig_fd = fd; | ||
5422 | sq->two_fd[i].moved_to = CLOSED; | ||
5382 | } | 5423 | } |
5383 | 5424 | ||
5384 | /* "hidden" fd is a fd used to read scripts, or a copy of such */ | ||
5385 | static int | 5425 | static int |
5386 | is_hidden_fd(struct redirtab *rp, int fd) | 5426 | save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq) |
5387 | { | 5427 | { |
5388 | int i; | 5428 | int i, new_fd; |
5389 | struct parsefile *pf; | 5429 | |
5430 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ | ||
5431 | avoid_fd = 9; | ||
5390 | 5432 | ||
5391 | if (fd == -1) | 5433 | #if JOBS |
5434 | if (fd == ttyfd) { | ||
5435 | /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */ | ||
5436 | ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd); | ||
5437 | TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd)); | ||
5438 | return 1; /* "we closed fd" */ | ||
5439 | } | ||
5440 | #endif | ||
5441 | /* Are we called from redirect(0)? E.g. redirect | ||
5442 | * in a forked child. No need to save fds, | ||
5443 | * we aren't going to use them anymore, ok to trash. | ||
5444 | */ | ||
5445 | if (!sq) | ||
5392 | return 0; | 5446 | return 0; |
5393 | /* Check open scripts' fds */ | 5447 | |
5394 | pf = g_parsefile; | 5448 | /* If this one of script's fds? */ |
5395 | while (pf) { | 5449 | if (fd != 0) { |
5396 | /* We skip pf_fd == 0 case because of the following case: | 5450 | struct parsefile *pf = g_parsefile; |
5397 | * $ ash # running ash interactively | 5451 | while (pf) { |
5398 | * $ . ./script.sh | 5452 | /* We skip fd == 0 case because of the following: |
5399 | * and in script.sh: "exec 9>&0". | 5453 | * $ ash # running ash interactively |
5400 | * Even though top-level pf_fd _is_ 0, | 5454 | * $ . ./script.sh |
5401 | * it's still ok to use it: "read" builtin uses it, | 5455 | * and in script.sh: "exec 9>&0". |
5402 | * why should we cripple "exec" builtin? | 5456 | * Even though top-level pf_fd _is_ 0, |
5403 | */ | 5457 | * it's still ok to use it: "read" builtin uses it, |
5404 | if (pf->pf_fd > 0 && fd == pf->pf_fd) { | 5458 | * why should we cripple "exec" builtin? |
5405 | return 1; | 5459 | */ |
5460 | if (fd == pf->pf_fd) { | ||
5461 | pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd); | ||
5462 | return 1; /* "we closed fd" */ | ||
5463 | } | ||
5464 | pf = pf->prev; | ||
5406 | } | 5465 | } |
5407 | pf = pf->prev; | ||
5408 | } | 5466 | } |
5409 | 5467 | ||
5410 | if (!rp) | 5468 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ |
5411 | return 0; | 5469 | |
5412 | /* Check saved fds of redirects */ | 5470 | /* First: do we collide with some already moved fds? */ |
5413 | fd |= COPYFD_RESTORE; | 5471 | for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) { |
5414 | for (i = 0; i < rp->pair_count; i++) { | 5472 | /* If we collide with an already moved fd... */ |
5415 | if (rp->two_fd[i].copy == fd) { | 5473 | if (fd == sq->two_fd[i].moved_to) { |
5416 | return 1; | 5474 | new_fd = fcntl_F_DUPFD(fd, avoid_fd); |
5475 | sq->two_fd[i].moved_to = new_fd; | ||
5476 | TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); | ||
5477 | if (new_fd < 0) /* what? */ | ||
5478 | xfunc_die(); | ||
5479 | return 0; /* "we did not close fd" */ | ||
5480 | } | ||
5481 | if (fd == sq->two_fd[i].orig_fd) { | ||
5482 | /* Example: echo Hello >/dev/null 1>&2 */ | ||
5483 | TRACE(("redirect_fd %d: already moved\n", fd)); | ||
5484 | return 0; /* "we did not close fd" */ | ||
5417 | } | 5485 | } |
5418 | } | 5486 | } |
5419 | return 0; | 5487 | |
5488 | /* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */ | ||
5489 | new_fd = fcntl_F_DUPFD(fd, avoid_fd); | ||
5490 | TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); | ||
5491 | if (new_fd < 0) { | ||
5492 | if (errno != EBADF) | ||
5493 | xfunc_die(); | ||
5494 | /* new_fd = CLOSED; - already is -1 */ | ||
5495 | } | ||
5496 | sq->two_fd[i].moved_to = new_fd; | ||
5497 | sq->two_fd[i].orig_fd = fd; | ||
5498 | |||
5499 | /* if we move stderr, let "set -x" code know */ | ||
5500 | if (fd == preverrout_fd) | ||
5501 | preverrout_fd = new_fd; | ||
5502 | |||
5503 | return 0; /* "we did not close fd" */ | ||
5420 | } | 5504 | } |
5421 | 5505 | ||
5422 | /* | 5506 | /* |
@@ -5431,109 +5515,80 @@ redirect(union node *redir, int flags) | |||
5431 | { | 5515 | { |
5432 | struct redirtab *sv; | 5516 | struct redirtab *sv; |
5433 | int sv_pos; | 5517 | int sv_pos; |
5434 | int fd; | ||
5435 | int newfd; | ||
5436 | 5518 | ||
5437 | if (!redir) | 5519 | if (!redir) |
5438 | return; | 5520 | return; |
5521 | |||
5522 | sv_pos = 0; | ||
5439 | sv = NULL; | 5523 | sv = NULL; |
5440 | INT_OFF; | 5524 | INT_OFF; |
5441 | if (flags & REDIR_PUSH) | 5525 | if (flags & REDIR_PUSH) |
5442 | sv = redirlist; | 5526 | sv = redirlist; |
5443 | sv_pos = 0; | ||
5444 | do { | 5527 | do { |
5445 | int right_fd = -1; | 5528 | int fd; |
5529 | int newfd; | ||
5530 | int close_fd; | ||
5531 | int closed; | ||
5532 | |||
5446 | fd = redir->nfile.fd; | 5533 | fd = redir->nfile.fd; |
5447 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { | 5534 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { |
5448 | right_fd = redir->ndup.dupfd; | 5535 | //bb_error_msg("doing %d > %d", fd, newfd); |
5449 | //bb_error_msg("doing %d > %d", fd, right_fd); | 5536 | newfd = redir->ndup.dupfd; |
5450 | /* redirect from/to same file descriptor? */ | 5537 | close_fd = -1; |
5451 | if (right_fd == fd) | ||
5452 | continue; | ||
5453 | /* "echo >&10" and 10 is a fd opened to a sh script? */ | ||
5454 | if (is_hidden_fd(sv, right_fd)) { | ||
5455 | errno = EBADF; /* as if it is closed */ | ||
5456 | ash_msg_and_raise_perror("%d", right_fd); | ||
5457 | } | ||
5458 | newfd = -1; | ||
5459 | } else { | 5538 | } else { |
5460 | newfd = openredirect(redir); /* always >= 0 */ | 5539 | newfd = openredirect(redir); /* always >= 0 */ |
5461 | if (fd == newfd) { | 5540 | if (fd == newfd) { |
5462 | /* Descriptor wasn't open before redirect. | 5541 | /* open() gave us precisely the fd we wanted. |
5463 | * Mark it for close in the future */ | 5542 | * This means that this fd was not busy |
5464 | if (need_to_remember(sv, fd)) { | 5543 | * (not opened to anywhere). |
5465 | goto remember_to_close; | 5544 | * Remember to close it on restore: |
5466 | } | 5545 | */ |
5546 | add_squirrel_closed(sv, fd); | ||
5467 | continue; | 5547 | continue; |
5468 | } | 5548 | } |
5549 | close_fd = newfd; | ||
5469 | } | 5550 | } |
5470 | #if BASH_REDIR_OUTPUT | 5551 | |
5471 | redirect_more: | 5552 | if (fd == newfd) |
5472 | #endif | 5553 | continue; |
5473 | if (need_to_remember(sv, fd)) { | 5554 | |
5474 | /* Copy old descriptor */ | 5555 | /* if "N>FILE": move newfd to fd */ |
5475 | /* Careful to not accidentally "save" | 5556 | /* if "N>&M": dup newfd to fd */ |
5476 | * to the same fd as right side fd in N>&M */ | 5557 | /* if "N>&-": close fd (newfd is -1) */ |
5477 | int minfd = right_fd < 10 ? 10 : right_fd + 1; | 5558 | |
5478 | #if defined(F_DUPFD_CLOEXEC) | 5559 | IF_BASH_REDIR_OUTPUT(redirect_more:) |
5479 | int copy = fcntl(fd, F_DUPFD_CLOEXEC, minfd); | 5560 | |
5480 | #else | 5561 | closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv); |
5481 | int copy = fcntl(fd, F_DUPFD, minfd); | 5562 | if (newfd == -1) { |
5482 | #endif | 5563 | /* "N>&-" means "close me" */ |
5483 | if (copy == -1) { | 5564 | if (!closed) { |
5484 | int e = errno; | 5565 | /* ^^^ optimization: saving may already |
5485 | if (e != EBADF) { | 5566 | * have closed it. If not... */ |
5486 | /* Strange error (e.g. "too many files" EMFILE?) */ | 5567 | close(fd); |
5487 | if (newfd >= 0) | ||
5488 | close(newfd); | ||
5489 | errno = e; | ||
5490 | ash_msg_and_raise_perror("%d", fd); | ||
5491 | /* NOTREACHED */ | ||
5492 | } | ||
5493 | /* EBADF: it is not open - good, remember to close it */ | ||
5494 | remember_to_close: | ||
5495 | copy = CLOSED; | ||
5496 | } else { /* fd is open, save its copy */ | ||
5497 | #if !defined(F_DUPFD_CLOEXEC) | ||
5498 | fcntl(copy, F_SETFD, FD_CLOEXEC); | ||
5499 | #endif | ||
5500 | /* "exec fd>&-" should not close fds | ||
5501 | * which point to script file(s). | ||
5502 | * Force them to be restored afterwards */ | ||
5503 | if (is_hidden_fd(sv, fd)) | ||
5504 | copy |= COPYFD_RESTORE; | ||
5505 | } | ||
5506 | /* if we move stderr, let "set -x" code know */ | ||
5507 | if (fd == preverrout_fd) | ||
5508 | preverrout_fd = copy; | ||
5509 | sv->two_fd[sv_pos].orig = fd; | ||
5510 | sv->two_fd[sv_pos].copy = copy; | ||
5511 | sv_pos++; | ||
5512 | } | ||
5513 | if (newfd < 0) { | ||
5514 | /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ | ||
5515 | if (redir->ndup.dupfd < 0) { /* "fd>&-" */ | ||
5516 | /* Don't want to trigger debugging */ | ||
5517 | if (fd != -1) | ||
5518 | close(fd); | ||
5519 | } else { | ||
5520 | dup2_or_raise(redir->ndup.dupfd, fd); | ||
5521 | } | 5568 | } |
5522 | } else if (fd != newfd) { /* move newfd to fd */ | 5569 | } else { |
5570 | ///TODO: if _newfd_ is a script fd or saved fd, then simulate EBADF! | ||
5571 | //if (newfd == ttyfd) { | ||
5572 | // errno = EBADF; | ||
5573 | // ash_msg_and_raise_perror("A %d", newfd); | ||
5574 | //} | ||
5575 | //if (newfd == g_parsefile->pf_fd) { | ||
5576 | // errno = EBADF; | ||
5577 | // ash_msg_and_raise_perror("B %d", newfd); | ||
5578 | //} | ||
5523 | dup2_or_raise(newfd, fd); | 5579 | dup2_or_raise(newfd, fd); |
5580 | if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */ | ||
5581 | close(close_fd); | ||
5524 | #if BASH_REDIR_OUTPUT | 5582 | #if BASH_REDIR_OUTPUT |
5525 | if (!(redir->nfile.type == NTO2 && fd == 2)) | 5583 | if (redir->nfile.type == NTO2 && fd == 1) { |
5584 | /* ">&FILE". we already redirected to 1, now copy 1 to 2 */ | ||
5585 | fd = 2; | ||
5586 | newfd = 1; | ||
5587 | close_fd = -1; | ||
5588 | goto redirect_more; | ||
5589 | } | ||
5526 | #endif | 5590 | #endif |
5527 | close(newfd); | ||
5528 | } | 5591 | } |
5529 | #if BASH_REDIR_OUTPUT | ||
5530 | if (redir->nfile.type == NTO2 && fd == 1) { | ||
5531 | /* We already redirected it to fd 1, now copy it to 2 */ | ||
5532 | newfd = 1; | ||
5533 | fd = 2; | ||
5534 | goto redirect_more; | ||
5535 | } | ||
5536 | #endif | ||
5537 | } while ((redir = redir->nfile.next) != NULL); | 5592 | } while ((redir = redir->nfile.next) != NULL); |
5538 | INT_ON; | 5593 | INT_ON; |
5539 | 5594 | ||
@@ -5542,7 +5597,7 @@ redirect(union node *redir, int flags) | |||
5542 | // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test | 5597 | // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test |
5543 | // triggers for pure REDIR_PUSH too. Thus, this is done almost always, | 5598 | // triggers for pure REDIR_PUSH too. Thus, this is done almost always, |
5544 | // not only for calls with flags containing REDIR_SAVEFD2. | 5599 | // not only for calls with flags containing REDIR_SAVEFD2. |
5545 | // We do this unconditionally (see code above). | 5600 | // We do this unconditionally (see save_fd_on_redirect()). |
5546 | //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) | 5601 | //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) |
5547 | // preverrout_fd = copied_fd2; | 5602 | // preverrout_fd = copied_fd2; |
5548 | } | 5603 | } |
@@ -5557,7 +5612,7 @@ redirectsafe(union node *redir, int flags) | |||
5557 | 5612 | ||
5558 | SAVE_INT(saveint); | 5613 | SAVE_INT(saveint); |
5559 | /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ | 5614 | /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ |
5560 | err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2; | 5615 | err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */ |
5561 | if (!err) { | 5616 | if (!err) { |
5562 | exception_handler = &jmploc; | 5617 | exception_handler = &jmploc; |
5563 | redirect(redir, flags); | 5618 | redirect(redir, flags); |
@@ -5591,7 +5646,7 @@ pushredir(union node *redir) | |||
5591 | sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0])); | 5646 | sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0])); |
5592 | sv->pair_count = i; | 5647 | sv->pair_count = i; |
5593 | while (--i >= 0) | 5648 | while (--i >= 0) |
5594 | sv->two_fd[i].orig = sv->two_fd[i].copy = EMPTY; | 5649 | sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY; |
5595 | sv->next = redirlist; | 5650 | sv->next = redirlist; |
5596 | redirlist = sv; | 5651 | redirlist = sv; |
5597 | return sv->next; | 5652 | return sv->next; |
@@ -5601,7 +5656,7 @@ pushredir(union node *redir) | |||
5601 | * Undo the effects of the last redirection. | 5656 | * Undo the effects of the last redirection. |
5602 | */ | 5657 | */ |
5603 | static void | 5658 | static void |
5604 | popredir(int drop, int restore) | 5659 | popredir(int drop) |
5605 | { | 5660 | { |
5606 | struct redirtab *rp; | 5661 | struct redirtab *rp; |
5607 | int i; | 5662 | int i; |
@@ -5611,20 +5666,19 @@ popredir(int drop, int restore) | |||
5611 | INT_OFF; | 5666 | INT_OFF; |
5612 | rp = redirlist; | 5667 | rp = redirlist; |
5613 | for (i = 0; i < rp->pair_count; i++) { | 5668 | for (i = 0; i < rp->pair_count; i++) { |
5614 | int fd = rp->two_fd[i].orig; | 5669 | int fd = rp->two_fd[i].orig_fd; |
5615 | int copy = rp->two_fd[i].copy; | 5670 | int copy = rp->two_fd[i].moved_to; |
5616 | if (copy == CLOSED) { | 5671 | if (copy == CLOSED) { |
5617 | if (!drop) | 5672 | if (!drop) |
5618 | close(fd); | 5673 | close(fd); |
5619 | continue; | 5674 | continue; |
5620 | } | 5675 | } |
5621 | if (copy != EMPTY) { | 5676 | if (copy != EMPTY) { |
5622 | if (!drop || (restore && (copy & COPYFD_RESTORE))) { | 5677 | if (!drop) { |
5623 | copy &= ~COPYFD_RESTORE; | ||
5624 | /*close(fd);*/ | 5678 | /*close(fd);*/ |
5625 | dup2_or_raise(copy, fd); | 5679 | dup2_or_raise(copy, fd); |
5626 | } | 5680 | } |
5627 | close(copy & ~COPYFD_RESTORE); | 5681 | close(copy); |
5628 | } | 5682 | } |
5629 | } | 5683 | } |
5630 | redirlist = rp->next; | 5684 | redirlist = rp->next; |
@@ -5636,7 +5690,7 @@ static void | |||
5636 | unwindredir(struct redirtab *stop) | 5690 | unwindredir(struct redirtab *stop) |
5637 | { | 5691 | { |
5638 | while (redirlist != stop) | 5692 | while (redirlist != stop) |
5639 | popredir(/*drop:*/ 0, /*restore:*/ 0); | 5693 | popredir(/*drop:*/ 0); |
5640 | } | 5694 | } |
5641 | 5695 | ||
5642 | 5696 | ||
@@ -7715,7 +7769,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
7715 | clearenv(); | 7769 | clearenv(); |
7716 | while (*envp) | 7770 | while (*envp) |
7717 | putenv(*envp++); | 7771 | putenv(*envp++); |
7718 | popredir(/*drop:*/ 1, /*restore:*/ 0); | 7772 | popredir(/*drop:*/ 1); |
7719 | run_applet_no_and_exit(applet_no, cmd, argv); | 7773 | run_applet_no_and_exit(applet_no, cmd, argv); |
7720 | } | 7774 | } |
7721 | /* re-exec ourselves with the new arguments */ | 7775 | /* re-exec ourselves with the new arguments */ |
@@ -8754,7 +8808,7 @@ evaltree(union node *n, int flags) | |||
8754 | status = evaltree(n->nredir.n, flags & EV_TESTED); | 8808 | status = evaltree(n->nredir.n, flags & EV_TESTED); |
8755 | } | 8809 | } |
8756 | if (n->nredir.redirect) | 8810 | if (n->nredir.redirect) |
8757 | popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); | 8811 | popredir(/*drop:*/ 0); |
8758 | goto setstatus; | 8812 | goto setstatus; |
8759 | case NCMD: | 8813 | case NCMD: |
8760 | evalfn = evalcommand; | 8814 | evalfn = evalcommand; |
@@ -9902,7 +9956,7 @@ evalcommand(union node *cmd, int flags) | |||
9902 | 9956 | ||
9903 | out: | 9957 | out: |
9904 | if (cmd->ncmd.redirect) | 9958 | if (cmd->ncmd.redirect) |
9905 | popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); | 9959 | popredir(/*drop:*/ cmd_is_exec); |
9906 | unwindredir(redir_stop); | 9960 | unwindredir(redir_stop); |
9907 | unwindlocalvars(localvar_stop); | 9961 | unwindlocalvars(localvar_stop); |
9908 | if (lastarg) { | 9962 | if (lastarg) { |
@@ -13727,7 +13781,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13727 | * Testcase: ash -c 'exec 1>&0' must not complain. */ | 13781 | * Testcase: ash -c 'exec 1>&0' must not complain. */ |
13728 | // if (!sflag) g_parsefile->pf_fd = -1; | 13782 | // if (!sflag) g_parsefile->pf_fd = -1; |
13729 | // ^^ not necessary since now we special-case fd 0 | 13783 | // ^^ not necessary since now we special-case fd 0 |
13730 | // in is_hidden_fd() to not be considered "hidden fd" | 13784 | // in save_fd_on_redirect() |
13731 | evalstring(minusc, sflag ? 0 : EV_EXIT); | 13785 | evalstring(minusc, sflag ? 0 : EV_EXIT); |
13732 | } | 13786 | } |
13733 | 13787 | ||
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests index ccc497d7b..740daa461 100755 --- a/shell/ash_test/ash-redir/redir_script.tests +++ b/shell/ash_test/ash-redir/redir_script.tests | |||
@@ -20,10 +20,15 @@ eval "find_fds $fds" | |||
20 | 20 | ||
21 | # Shell should not lose that fd. Did it? | 21 | # Shell should not lose that fd. Did it? |
22 | find_fds | 22 | find_fds |
23 | test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } | 23 | test x"$fds1" = x"$fds" \ |
24 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
25 | |||
26 | # One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11: | ||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | ||
28 | test x"$fds" = x" 11>&- 3>&-" \ | ||
29 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
24 | 30 | ||
25 | echo "Bug: script fd is closed" | 31 | echo "Bug: script fd is closed" |
26 | echo "fds1:$fds1" | 32 | echo "fds1:$fds1" |
27 | echo "fds2:$fds" | 33 | echo "fds2:$fds" |
28 | exit 1 | 34 | exit 1 |
29 | |||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.right b/shell/ash_test/ash-redir/redir_to_bad_fd255.right new file mode 100644 index 000000000..9c5e35b36 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ./redir_to_bad_fd255.tests: line 2: 255: Bad file descriptor | ||
2 | OK | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.tests b/shell/ash_test/ash-redir/redir_to_bad_fd255.tests new file mode 100755 index 000000000..2266af6da --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&255 | ||
3 | echo OK | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.right b/shell/ash_test/ash-redir/redir_to_bad_fd3.right new file mode 100644 index 000000000..895a4a0a6 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ./redir_to_bad_fd3.tests: line 2: 3: Bad file descriptor | ||
2 | OK | ||
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.tests b/shell/ash_test/ash-redir/redir_to_bad_fd3.tests new file mode 100755 index 000000000..98c54cfc6 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&3 | ||
3 | echo OK | ||
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests index ccc497d7b..740daa461 100755 --- a/shell/hush_test/hush-redir/redir_script.tests +++ b/shell/hush_test/hush-redir/redir_script.tests | |||
@@ -20,10 +20,15 @@ eval "find_fds $fds" | |||
20 | 20 | ||
21 | # Shell should not lose that fd. Did it? | 21 | # Shell should not lose that fd. Did it? |
22 | find_fds | 22 | find_fds |
23 | test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } | 23 | test x"$fds1" = x"$fds" \ |
24 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
25 | |||
26 | # One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11: | ||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | ||
28 | test x"$fds" = x" 11>&- 3>&-" \ | ||
29 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
24 | 30 | ||
25 | echo "Bug: script fd is closed" | 31 | echo "Bug: script fd is closed" |
26 | echo "fds1:$fds1" | 32 | echo "fds1:$fds1" |
27 | echo "fds2:$fds" | 33 | echo "fds2:$fds" |
28 | exit 1 | 34 | exit 1 |
29 | |||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.right b/shell/hush_test/hush-redir/redir_to_bad_fd255.right new file mode 100644 index 000000000..936911ce5 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.right | |||
@@ -0,0 +1 @@ | |||
hush: can't duplicate file descriptor: Bad file descriptor | |||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.tests b/shell/hush_test/hush-redir/redir_to_bad_fd255.tests new file mode 100755 index 000000000..2266af6da --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&255 | ||
3 | echo OK | ||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.right b/shell/hush_test/hush-redir/redir_to_bad_fd3.right new file mode 100644 index 000000000..936911ce5 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.right | |||
@@ -0,0 +1 @@ | |||
hush: can't duplicate file descriptor: Bad file descriptor | |||
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.tests b/shell/hush_test/hush-redir/redir_to_bad_fd3.tests new file mode 100755 index 000000000..98c54cfc6 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | # ash uses fd 10 (usually) for reading the script | ||
2 | echo LOST >&3 | ||
3 | echo OK | ||