diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-30 23:34:04 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-31 04:08:09 +0200 |
commit | 657e9005a9e31e1da094b260abaa8f335e92301f (patch) | |
tree | 6ee40b817a62ef8989532de792bb3a6d13cd19da | |
parent | d07a15bd1ba99caa9386234cda1e8c83897c7553 (diff) | |
download | busybox-w32-657e9005a9e31e1da094b260abaa8f335e92301f.tar.gz busybox-w32-657e9005a9e31e1da094b260abaa8f335e92301f.tar.bz2 busybox-w32-657e9005a9e31e1da094b260abaa8f335e92301f.zip |
hush: massage redirect code to be slightly more like ash
function old new delta
save_fd_on_redirect - 203 +203
xdup_CLOEXEC_and_close - 75 +75
setup_redirects 245 250 +5
xdup_and_close 72 - -72
save_fds_on_redirect 221 - -221
------------------------------------------------------------------------------
(add/remove: 2/2 grow/shrink: 1/0 up/down: 283/-293) Total: -10 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 109 |
1 files changed, 72 insertions, 37 deletions
diff --git a/shell/hush.c b/shell/hush.c index 0fae809b3..d4101d66f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1454,11 +1454,11 @@ static int fcntl_F_DUPFD(int fd, int avoid_fd) | |||
1454 | return newfd; | 1454 | return newfd; |
1455 | } | 1455 | } |
1456 | 1456 | ||
1457 | static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) | 1457 | static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) |
1458 | { | 1458 | { |
1459 | int newfd; | 1459 | int newfd; |
1460 | repeat: | 1460 | repeat: |
1461 | newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, avoid_fd + 1); | 1461 | newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); |
1462 | if (newfd < 0) { | 1462 | if (newfd < 0) { |
1463 | if (errno == EBUSY) | 1463 | if (errno == EBUSY) |
1464 | goto repeat; | 1464 | goto repeat; |
@@ -1469,6 +1469,8 @@ static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC, int avoid_fd) | |||
1469 | return fd; | 1469 | return fd; |
1470 | xfunc_die(); | 1470 | xfunc_die(); |
1471 | } | 1471 | } |
1472 | if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ | ||
1473 | fcntl(newfd, F_SETFD, FD_CLOEXEC); | ||
1472 | close(fd); | 1474 | close(fd); |
1473 | return newfd; | 1475 | return newfd; |
1474 | } | 1476 | } |
@@ -1507,7 +1509,7 @@ static int save_FILEs_on_redirect(int fd, int avoid_fd) | |||
1507 | while (fl) { | 1509 | while (fl) { |
1508 | if (fd == fl->fd) { | 1510 | if (fd == fl->fd) { |
1509 | /* We use it only on script files, they are all CLOEXEC */ | 1511 | /* We use it only on script files, they are all CLOEXEC */ |
1510 | fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC, avoid_fd); | 1512 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); |
1511 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); | 1513 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); |
1512 | return 1; | 1514 | return 1; |
1513 | } | 1515 | } |
@@ -6711,18 +6713,42 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6711 | return append_squirrel(sq, i, fd, moved_to); | 6713 | return append_squirrel(sq, i, fd, moved_to); |
6712 | } | 6714 | } |
6713 | 6715 | ||
6716 | static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd) | ||
6717 | { | ||
6718 | int i; | ||
6719 | |||
6720 | i = 0; | ||
6721 | if (sq) while (sq[i].orig_fd >= 0) { | ||
6722 | /* If we collide with an already moved fd... */ | ||
6723 | if (fd == sq[i].orig_fd) { | ||
6724 | /* Examples: | ||
6725 | * "echo 3>FILE 3>&- 3>FILE" | ||
6726 | * "echo 3>&- 3>FILE" | ||
6727 | * No need for last redirect to insert | ||
6728 | * another "need to close 3" indicator. | ||
6729 | */ | ||
6730 | debug_printf_redir("redirect_fd %d: already moved or closed\n", fd); | ||
6731 | return sq; | ||
6732 | } | ||
6733 | i++; | ||
6734 | } | ||
6735 | |||
6736 | debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd); | ||
6737 | return append_squirrel(sq, i, fd, -1); | ||
6738 | } | ||
6739 | |||
6714 | /* fd: redirect wants this fd to be used (e.g. 3>file). | 6740 | /* fd: redirect wants this fd to be used (e.g. 3>file). |
6715 | * Move all conflicting internally used fds, | 6741 | * Move all conflicting internally used fds, |
6716 | * and remember them so that we can restore them later. | 6742 | * and remember them so that we can restore them later. |
6717 | */ | 6743 | */ |
6718 | static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | 6744 | static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) |
6719 | { | 6745 | { |
6720 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ | 6746 | if (avoid_fd < 9) /* the important case here is that it can be -1 */ |
6721 | avoid_fd = 9; | 6747 | avoid_fd = 9; |
6722 | 6748 | ||
6723 | #if ENABLE_HUSH_INTERACTIVE | 6749 | #if ENABLE_HUSH_INTERACTIVE |
6724 | if (fd != 0 && fd == G.interactive_fd) { | 6750 | if (fd != 0 && fd == G.interactive_fd) { |
6725 | G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC, avoid_fd); | 6751 | G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd); |
6726 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); | 6752 | debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd); |
6727 | return 1; /* "we closed fd" */ | 6753 | return 1; /* "we closed fd" */ |
6728 | } | 6754 | } |
@@ -6748,7 +6774,6 @@ static int save_fds_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
6748 | 6774 | ||
6749 | static void restore_redirects(struct squirrel *sq) | 6775 | static void restore_redirects(struct squirrel *sq) |
6750 | { | 6776 | { |
6751 | |||
6752 | if (sq) { | 6777 | if (sq) { |
6753 | int i = 0; | 6778 | int i = 0; |
6754 | while (sq[i].orig_fd >= 0) { | 6779 | while (sq[i].orig_fd >= 0) { |
@@ -6775,13 +6800,15 @@ static void restore_redirects(struct squirrel *sq) | |||
6775 | * and stderr if they are redirected. */ | 6800 | * and stderr if they are redirected. */ |
6776 | static int setup_redirects(struct command *prog, struct squirrel **sqp) | 6801 | static int setup_redirects(struct command *prog, struct squirrel **sqp) |
6777 | { | 6802 | { |
6778 | int openfd, mode; | ||
6779 | struct redir_struct *redir; | 6803 | struct redir_struct *redir; |
6780 | 6804 | ||
6781 | for (redir = prog->redirects; redir; redir = redir->next) { | 6805 | for (redir = prog->redirects; redir; redir = redir->next) { |
6806 | int newfd; | ||
6807 | int closed; | ||
6808 | |||
6782 | if (redir->rd_type == REDIRECT_HEREDOC2) { | 6809 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
6783 | /* "rd_fd<<HERE" case */ | 6810 | /* "rd_fd<<HERE" case */ |
6784 | save_fds_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); | 6811 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); |
6785 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 6812 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
6786 | * of the heredoc */ | 6813 | * of the heredoc */ |
6787 | debug_printf_parse("set heredoc '%s'\n", | 6814 | debug_printf_parse("set heredoc '%s'\n", |
@@ -6793,6 +6820,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6793 | if (redir->rd_dup == REDIRFD_TO_FILE) { | 6820 | if (redir->rd_dup == REDIRFD_TO_FILE) { |
6794 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ | 6821 | /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */ |
6795 | char *p; | 6822 | char *p; |
6823 | int mode; | ||
6824 | |||
6796 | if (redir->rd_filename == NULL) { | 6825 | if (redir->rd_filename == NULL) { |
6797 | /* | 6826 | /* |
6798 | * Examples: | 6827 | * Examples: |
@@ -6804,9 +6833,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6804 | } | 6833 | } |
6805 | mode = redir_table[redir->rd_type].mode; | 6834 | mode = redir_table[redir->rd_type].mode; |
6806 | p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); | 6835 | p = expand_string_to_string(redir->rd_filename, /*unbackslash:*/ 1); |
6807 | openfd = open_or_warn(p, mode); | 6836 | newfd = open_or_warn(p, mode); |
6808 | free(p); | 6837 | free(p); |
6809 | if (openfd < 0) { | 6838 | if (newfd < 0) { |
6810 | /* Error message from open_or_warn can be lost | 6839 | /* Error message from open_or_warn can be lost |
6811 | * if stderr has been redirected, but bash | 6840 | * if stderr has been redirected, but bash |
6812 | * and ash both lose it as well | 6841 | * and ash both lose it as well |
@@ -6814,40 +6843,46 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6814 | */ | 6843 | */ |
6815 | return 1; | 6844 | return 1; |
6816 | } | 6845 | } |
6817 | if (openfd == redir->rd_fd && sqp) { | 6846 | if (newfd == redir->rd_fd && sqp) { |
6818 | /* open() gave us precisely the fd we wanted. | 6847 | /* open() gave us precisely the fd we wanted. |
6819 | * This means that this fd was not busy | 6848 | * This means that this fd was not busy |
6820 | * (not opened to anywhere). | 6849 | * (not opened to anywhere). |
6821 | * Remember to close it on restore: | 6850 | * Remember to close it on restore: |
6822 | */ | 6851 | */ |
6823 | struct squirrel *sq = *sqp; | 6852 | *sqp = add_squirrel_closed(*sqp, newfd); |
6824 | int i = 0; | 6853 | debug_printf_redir("redir to previously closed fd %d\n", newfd); |
6825 | if (sq) while (sq[i].orig_fd >= 0) | ||
6826 | i++; | ||
6827 | *sqp = append_squirrel(sq, i, openfd, -1); /* -1 = "it was closed" */ | ||
6828 | debug_printf_redir("redir to previously closed fd %d\n", openfd); | ||
6829 | } | 6854 | } |
6830 | } else { | 6855 | } else { |
6831 | /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */ | 6856 | /* "rd_fd>&rd_dup" or "rd_fd>&-" case */ |
6832 | openfd = redir->rd_dup; | 6857 | newfd = redir->rd_dup; |
6833 | } | 6858 | } |
6834 | 6859 | ||
6835 | if (openfd != redir->rd_fd) { | 6860 | if (newfd == redir->rd_fd) |
6836 | int closed = save_fds_on_redirect(redir->rd_fd, /*avoid:*/ openfd, sqp); | 6861 | continue; |
6837 | if (openfd == REDIRFD_CLOSE) { | 6862 | |
6838 | /* "rd_fd >&-" means "close me" */ | 6863 | /* if "N>FILE": move newfd to redir->rd_fd */ |
6839 | if (!closed) { | 6864 | /* if "N>&M": dup newfd to redir->rd_fd */ |
6840 | /* ^^^ optimization: saving may already | 6865 | /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */ |
6841 | * have closed it. If not... */ | 6866 | |
6842 | close(redir->rd_fd); | 6867 | closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp); |
6843 | } | 6868 | if (newfd == REDIRFD_CLOSE) { |
6844 | } else { | 6869 | /* "N>&-" means "close me" */ |
6845 | xdup2(openfd, redir->rd_fd); | 6870 | if (!closed) { |
6846 | if (redir->rd_dup == REDIRFD_TO_FILE) | 6871 | /* ^^^ optimization: saving may already |
6847 | /* "rd_fd > FILE" */ | 6872 | * have closed it. If not... */ |
6848 | close(openfd); | 6873 | close(redir->rd_fd); |
6849 | /* else: "rd_fd > rd_dup" */ | 6874 | } |
6850 | } | 6875 | /* Sometimes we do another close on restore, getting EBADF. |
6876 | * Consider "echo 3>FILE 3>&-" | ||
6877 | * first redirect remembers "need to close 3", | ||
6878 | * and second redirect closes 3! Restore code then closes 3 again. | ||
6879 | */ | ||
6880 | } else { | ||
6881 | xdup2(newfd, redir->rd_fd); | ||
6882 | if (redir->rd_dup == REDIRFD_TO_FILE) | ||
6883 | /* "rd_fd > FILE" */ | ||
6884 | close(newfd); | ||
6885 | /* else: "rd_fd > rd_dup" */ | ||
6851 | } | 6886 | } |
6852 | } | 6887 | } |
6853 | return 0; | 6888 | return 0; |