aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-07-24 16:54:41 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-07-24 16:54:41 +0200
commit41ef41b3e0a16c9f8524870a2dc4f768c237939e (patch)
tree01feb4fb17e3572f157d4f4e6e3a1de3dc4afa61
parentdfc739476343244371636d58561f7b743faf50d6 (diff)
downloadbusybox-w32-41ef41b3e0a16c9f8524870a2dc4f768c237939e.tar.gz
busybox-w32-41ef41b3e0a16c9f8524870a2dc4f768c237939e.tar.bz2
busybox-w32-41ef41b3e0a16c9f8524870a2dc4f768c237939e.zip
hush: fix nested redirects colliding with script fds
This necessitates switch from libc FILE api to a simple homegrown replacement. The change which fixes the bug here is the deleting of restore_redirected_FILEs(); line. It was prematurely moving (restoring) script fd#3. The fix is: we don't even _want_ to restore scrit fds, we are perfectly fine with them being moved. The only reason we tried to restore them is that FILE api did not allow moving of FILE->fd. function old new delta refill_HFILE_and_getc - 93 +93 hfopen - 90 +90 hfclose - 66 +66 pseudo_exec_argv 591 597 +6 hush_main 1089 1095 +6 builtin_source 209 214 +5 save_fd_on_redirect 197 200 +3 setup_redirects 320 321 +1 fgetc_interactive 235 236 +1 i_peek_and_eat_bkslash_nl 99 97 -2 expand_vars_to_list 1103 1100 -3 restore_redirects 99 52 -47 fclose_and_forget 57 - -57 remember_FILE 63 - -63 ------------------------------------------------------------------------------ (add/remove: 3/2 grow/shrink: 6/3 up/down: 271/-172) Total: 99 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--libbb/xfuncs_printf.c1
-rw-r--r--shell/ash_test/ash-heredoc/heredocB.right3
-rwxr-xr-xshell/ash_test/ash-heredoc/heredocB.tests12
-rwxr-xr-xshell/ash_test/ash-redir/redir_script.tests4
-rw-r--r--shell/hush.c234
-rw-r--r--shell/hush_test/hush-heredoc/heredocB.right3
-rwxr-xr-xshell/hush_test/hush-heredoc/heredocB.tests12
-rwxr-xr-xshell/hush_test/hush-redir/redir_script.tests4
8 files changed, 175 insertions, 98 deletions
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index 7247c915b..6cc60f6c0 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -222,6 +222,7 @@ void FAST_FUNC xdup2(int from, int to)
222{ 222{
223 if (dup2(from, to) != to) 223 if (dup2(from, to) != to)
224 bb_perror_msg_and_die("can't duplicate file descriptor"); 224 bb_perror_msg_and_die("can't duplicate file descriptor");
225 // " %d to %d", from, to);
225} 226}
226 227
227// "Renumber" opened fd 228// "Renumber" opened fd
diff --git a/shell/ash_test/ash-heredoc/heredocB.right b/shell/ash_test/ash-heredoc/heredocB.right
new file mode 100644
index 000000000..43ba0b4f9
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredocB.right
@@ -0,0 +1,3 @@
1one - alpha
2two - beta
3three - gamma
diff --git a/shell/ash_test/ash-heredoc/heredocB.tests b/shell/ash_test/ash-heredoc/heredocB.tests
new file mode 100755
index 000000000..45ea4687f
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredocB.tests
@@ -0,0 +1,12 @@
1while read line1; do
2 read line2 <&3
3 echo $line1 - $line2
4done <<EOF1 3<<EOF2
5one
6two
7three
8EOF1
9alpha
10beta
11gamma
12EOF2
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests
index 740daa461..a8d93ce4f 100755
--- a/shell/ash_test/ash-redir/redir_script.tests
+++ b/shell/ash_test/ash-redir/redir_script.tests
@@ -27,6 +27,10 @@ test x"$fds1" = x"$fds" \
27test x"$fds1" = x" 10>&- 3>&-" && \ 27test x"$fds1" = x" 10>&- 3>&-" && \
28test x"$fds" = x" 11>&- 3>&-" \ 28test x"$fds" = x" 11>&- 3>&-" \
29&& { echo "Ok: script fd is not closed"; exit 0; } 29&& { echo "Ok: script fd is not closed"; exit 0; }
30# or we see that fd 3 moved to fd 10:
31test x"$fds1" = x" 3>&- 4>&-" && \
32test x"$fds" = x" 10>&- 3>&-" \
33&& { echo "Ok: script fd is not closed"; exit 0; }
30 34
31echo "Bug: script fd is closed" 35echo "Bug: script fd is closed"
32echo "fds1:$fds1" 36echo "fds1:$fds1"
diff --git a/shell/hush.c b/shell/hush.c
index c26484b49..7a1513c24 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -357,6 +357,9 @@
357#else 357#else
358# define CLEAR_RANDOM_T(rnd) ((void)0) 358# define CLEAR_RANDOM_T(rnd) ((void)0)
359#endif 359#endif
360#ifndef O_CLOEXEC
361# define O_CLOEXEC 0
362#endif
360#ifndef F_DUPFD_CLOEXEC 363#ifndef F_DUPFD_CLOEXEC
361# define F_DUPFD_CLOEXEC F_DUPFD 364# define F_DUPFD_CLOEXEC F_DUPFD
362#endif 365#endif
@@ -556,11 +559,27 @@ static const char *const assignment_flag[] = {
556}; 559};
557#endif 560#endif
558 561
562/* We almost can use standard FILE api, but we need an ability to move
563 * its fd when redirects coincide with it. No api exists for that
564 * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902).
565 * HFILE is our internal alternative. Only supports reading.
566 * Since we now can, we incorporate linked list of all opened HFILEs
567 * into the struct (used to be a separate mini-list).
568 */
569typedef struct HFILE {
570 char *cur;
571 char *end;
572 struct HFILE *next_hfile;
573 int is_stdin;
574 int fd;
575 char buf[1024];
576} HFILE;
577
559typedef struct in_str { 578typedef struct in_str {
560 const char *p; 579 const char *p;
561 int peek_buf[2]; 580 int peek_buf[2];
562 int last_char; 581 int last_char;
563 FILE *file; 582 HFILE *file;
564} in_str; 583} in_str;
565 584
566/* The descrip member of this structure is only used to make 585/* The descrip member of this structure is only used to make
@@ -814,14 +833,6 @@ enum {
814 NUM_OPT_O 833 NUM_OPT_O
815}; 834};
816 835
817
818struct FILE_list {
819 struct FILE_list *next;
820 FILE *fp;
821 int fd;
822};
823
824
825/* "Globals" within this file */ 836/* "Globals" within this file */
826/* Sorted roughly by size (smaller offsets == smaller code) */ 837/* Sorted roughly by size (smaller offsets == smaller code) */
827struct globals { 838struct globals {
@@ -954,7 +965,7 @@ struct globals {
954 unsigned lineno; 965 unsigned lineno;
955 char *lineno_var; 966 char *lineno_var;
956#endif 967#endif
957 struct FILE_list *FILE_list; 968 HFILE *HFILE_list;
958 /* Which signals have non-DFL handler (even with no traps set)? 969 /* Which signals have non-DFL handler (even with no traps set)?
959 * Set at the start to: 970 * Set at the start to:
960 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) 971 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
@@ -1563,83 +1574,115 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1563} 1574}
1564 1575
1565 1576
1566/* Manipulating the list of open FILEs */ 1577/* Manipulating HFILEs */
1567static FILE *remember_FILE(FILE *fp) 1578static HFILE *hfopen(const char *name)
1568{ 1579{
1569 if (fp) { 1580 HFILE *fp;
1570 struct FILE_list *n = xmalloc(sizeof(*n)); 1581 int fd;
1571 n->next = G.FILE_list; 1582
1572 G.FILE_list = n; 1583 fd = STDIN_FILENO;
1573 n->fp = fp; 1584 if (name) {
1574 n->fd = fileno(fp); 1585 fd = open(name, O_RDONLY | O_CLOEXEC);
1575 close_on_exec_on(n->fd); 1586 if (fd < 0)
1587 return NULL;
1588 if (O_CLOEXEC == 0) /* ancient libc */
1589 close_on_exec_on(fd);
1576 } 1590 }
1591
1592 fp = xmalloc(sizeof(*fp));
1593 fp->is_stdin = (name == NULL);
1594 fp->fd = fd;
1595 fp->cur = fp->end = fp->buf;
1596 fp->next_hfile = G.HFILE_list;
1597 G.HFILE_list = fp;
1577 return fp; 1598 return fp;
1578} 1599}
1579static void fclose_and_forget(FILE *fp) 1600static void hfclose(HFILE *fp)
1580{ 1601{
1581 struct FILE_list **pp = &G.FILE_list; 1602 HFILE **pp = &G.HFILE_list;
1582 while (*pp) { 1603 while (*pp) {
1583 struct FILE_list *cur = *pp; 1604 HFILE *cur = *pp;
1584 if (cur->fp == fp) { 1605 if (cur == fp) {
1585 *pp = cur->next; 1606 *pp = cur->next_hfile;
1586 free(cur);
1587 break; 1607 break;
1588 } 1608 }
1589 pp = &cur->next; 1609 pp = &cur->next_hfile;
1590 } 1610 }
1591 fclose(fp); 1611 if (fp->fd >= 0)
1612 close(fp->fd);
1613 free(fp);
1592} 1614}
1593static int save_FILEs_on_redirect(int fd, int avoid_fd) 1615static int refill_HFILE_and_getc(HFILE *fp)
1594{ 1616{
1595 struct FILE_list *fl = G.FILE_list; 1617 int n;
1618
1619 if (fp->fd < 0) {
1620 /* Already saw EOF */
1621 return EOF;
1622 }
1623 /* Try to buffer more input */
1624 fp->cur = fp->buf;
1625 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf));
1626 if (n < 0) {
1627 bb_perror_msg("read error");
1628 n = 0;
1629 }
1630 fp->end = fp->buf + n;
1631 if (n == 0) {
1632 /* EOF/error */
1633 close(fp->fd);
1634 fp->fd = -1;
1635 return EOF;
1636 }
1637 return (unsigned char)(*fp->cur++);
1638}
1639/* Inlined for common case of non-empty buffer.
1640 */
1641static ALWAYS_INLINE int hfgetc(HFILE *fp)
1642{
1643 if (fp->cur < fp->end)
1644 return (unsigned char)(*fp->cur++);
1645 /* Buffer empty */
1646 return refill_HFILE_and_getc(fp);
1647}
1648static int move_HFILEs_on_redirect(int fd, int avoid_fd)
1649{
1650 HFILE *fl = G.HFILE_list;
1596 while (fl) { 1651 while (fl) {
1597 if (fd == fl->fd) { 1652 if (fd == fl->fd) {
1598 /* We use it only on script files, they are all CLOEXEC */ 1653 /* We use it only on script files, they are all CLOEXEC */
1599 fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); 1654 fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1600 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); 1655 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd);
1601 return 1; 1656 return 1; /* "found and moved" */
1602 }
1603 fl = fl->next;
1604 }
1605 return 0;
1606}
1607static void restore_redirected_FILEs(void)
1608{
1609 struct FILE_list *fl = G.FILE_list;
1610 while (fl) {
1611 int should_be = fileno(fl->fp);
1612 if (fl->fd != should_be) {
1613 debug_printf_redir("restoring script fd from %d to %d\n", fl->fd, should_be);
1614 xmove_fd(fl->fd, should_be);
1615 fl->fd = should_be;
1616 } 1657 }
1617 fl = fl->next; 1658 fl = fl->next_hfile;
1618 } 1659 }
1660 return 0; /* "not in the list" */
1619} 1661}
1620#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU 1662#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
1621static void close_all_FILE_list(void) 1663static void close_all_HFILE_list(void)
1622{ 1664{
1623 struct FILE_list *fl = G.FILE_list; 1665 HFILE *fl = G.HFILE_list;
1624 while (fl) { 1666 while (fl) {
1625 /* fclose would also free FILE object. 1667 /* hfclose would also free HFILE object.
1626 * It is disastrous if we share memory with a vforked parent. 1668 * It is disastrous if we share memory with a vforked parent.
1627 * I'm not sure we never come here after vfork. 1669 * I'm not sure we never come here after vfork.
1628 * Therefore just close fd, nothing more. 1670 * Therefore just close fd, nothing more.
1629 */ 1671 */
1630 /*fclose(fl->fp); - unsafe */ 1672 /*hfclose(fl); - unsafe */
1631 close(fl->fd); 1673 if (fl->fd >= 0)
1632 fl = fl->next; 1674 close(fl->fd);
1675 fl = fl->next_hfile;
1633 } 1676 }
1634} 1677}
1635#endif 1678#endif
1636static int fd_in_FILEs(int fd) 1679static int fd_in_HFILEs(int fd)
1637{ 1680{
1638 struct FILE_list *fl = G.FILE_list; 1681 HFILE *fl = G.HFILE_list;
1639 while (fl) { 1682 while (fl) {
1640 if (fl->fd == fd) 1683 if (fl->fd == fd)
1641 return 1; 1684 return 1;
1642 fl = fl->next; 1685 fl = fl->next_hfile;
1643 } 1686 }
1644 return 0; 1687 return 0;
1645} 1688}
@@ -2580,7 +2623,7 @@ static int get_user_input(struct in_str *i)
2580 } 2623 }
2581 fflush_all(); 2624 fflush_all();
2582//FIXME: here ^C or SIGINT will have effect only after <Enter> 2625//FIXME: here ^C or SIGINT will have effect only after <Enter>
2583 r = fgetc(i->file); 2626 r = hfgetc(i->file);
2584 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input, 2627 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
2585 * no ^C masking happens during fgetc, no special code for ^C: 2628 * no ^C masking happens during fgetc, no special code for ^C:
2586 * it generates SIGINT as usual. 2629 * it generates SIGINT as usual.
@@ -2600,22 +2643,22 @@ static int fgetc_interactive(struct in_str *i)
2600{ 2643{
2601 int ch; 2644 int ch;
2602 /* If it's interactive stdin, get new line. */ 2645 /* If it's interactive stdin, get new line. */
2603 if (G_interactive_fd && i->file == stdin) { 2646 if (G_interactive_fd && i->file->is_stdin) {
2604 /* Returns first char (or EOF), the rest is in i->p[] */ 2647 /* Returns first char (or EOF), the rest is in i->p[] */
2605 ch = get_user_input(i); 2648 ch = get_user_input(i);
2606 G.promptmode = 1; /* PS2 */ 2649 G.promptmode = 1; /* PS2 */
2607 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); 2650 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
2608 } else { 2651 } else {
2609 /* Not stdin: script file, sourced file, etc */ 2652 /* Not stdin: script file, sourced file, etc */
2610 do ch = fgetc(i->file); while (ch == '\0'); 2653 do ch = hfgetc(i->file); while (ch == '\0');
2611 } 2654 }
2612 return ch; 2655 return ch;
2613} 2656}
2614#else 2657#else
2615static inline int fgetc_interactive(struct in_str *i) 2658static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
2616{ 2659{
2617 int ch; 2660 int ch;
2618 do ch = fgetc(i->file); while (ch == '\0'); 2661 do ch = hfgetc(i->file); while (ch == '\0');
2619 return ch; 2662 return ch;
2620} 2663}
2621#endif /* INTERACTIVE */ 2664#endif /* INTERACTIVE */
@@ -2730,7 +2773,7 @@ static int i_peek2(struct in_str *i)
2730 ch = i->peek_buf[1]; 2773 ch = i->peek_buf[1];
2731 if (ch == 0) { 2774 if (ch == 0) {
2732 /* We did not read it yet, get it now */ 2775 /* We did not read it yet, get it now */
2733 do ch = fgetc(i->file); while (ch == '\0'); 2776 do ch = hfgetc(i->file); while (ch == '\0');
2734 i->peek_buf[1] = ch; 2777 i->peek_buf[1] = ch;
2735 } 2778 }
2736 2779
@@ -2774,10 +2817,10 @@ static int i_peek_and_eat_bkslash_nl(struct in_str *input)
2774 } 2817 }
2775} 2818}
2776 2819
2777static void setup_file_in_str(struct in_str *i, FILE *f) 2820static void setup_file_in_str(struct in_str *i, HFILE *fp)
2778{ 2821{
2779 memset(i, 0, sizeof(*i)); 2822 memset(i, 0, sizeof(*i));
2780 i->file = f; 2823 i->file = fp;
2781 /* i->p = NULL; */ 2824 /* i->p = NULL; */
2782} 2825}
2783 2826
@@ -7106,19 +7149,19 @@ static void parse_and_run_string(const char *s)
7106 //IF_HUSH_LINENO_VAR(G.lineno = sv;) 7149 //IF_HUSH_LINENO_VAR(G.lineno = sv;)
7107} 7150}
7108 7151
7109static void parse_and_run_file(FILE *f) 7152static void parse_and_run_file(HFILE *fp)
7110{ 7153{
7111 struct in_str input; 7154 struct in_str input;
7112 IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) 7155 IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;)
7113 7156
7114 IF_HUSH_LINENO_VAR(G.lineno = 1;) 7157 IF_HUSH_LINENO_VAR(G.lineno = 1;)
7115 setup_file_in_str(&input, f); 7158 setup_file_in_str(&input, fp);
7116 parse_and_run_stream(&input, ';'); 7159 parse_and_run_stream(&input, ';');
7117 IF_HUSH_LINENO_VAR(G.lineno = sv;) 7160 IF_HUSH_LINENO_VAR(G.lineno = sv;)
7118} 7161}
7119 7162
7120#if ENABLE_HUSH_TICK 7163#if ENABLE_HUSH_TICK
7121static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) 7164static int generate_stream_from_string(const char *s, pid_t *pid_p)
7122{ 7165{
7123 pid_t pid; 7166 pid_t pid;
7124 int channel[2]; 7167 int channel[2];
@@ -7220,7 +7263,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
7220 free(to_free); 7263 free(to_free);
7221# endif 7264# endif
7222 close(channel[1]); 7265 close(channel[1]);
7223 return remember_FILE(xfdopen_for_read(channel[0])); 7266 return channel[0];
7224} 7267}
7225 7268
7226/* Return code is exit status of the process that is run. */ 7269/* Return code is exit status of the process that is run. */
@@ -7230,7 +7273,7 @@ static int process_command_subs(o_string *dest, const char *s)
7230 pid_t pid; 7273 pid_t pid;
7231 int status, ch, eol_cnt; 7274 int status, ch, eol_cnt;
7232 7275
7233 fp = generate_stream_from_string(s, &pid); 7276 fp = xfdopen_for_read(generate_stream_from_string(s, &pid));
7234 7277
7235 /* Now send results of command back into original context */ 7278 /* Now send results of command back into original context */
7236 eol_cnt = 0; 7279 eol_cnt = 0;
@@ -7249,7 +7292,7 @@ static int process_command_subs(o_string *dest, const char *s)
7249 } 7292 }
7250 7293
7251 debug_printf("done reading from `cmd` pipe, closing it\n"); 7294 debug_printf("done reading from `cmd` pipe, closing it\n");
7252 fclose_and_forget(fp); 7295 fclose(fp);
7253 /* We need to extract exitcode. Test case 7296 /* We need to extract exitcode. Test case
7254 * "true; echo `sleep 1; false` $?" 7297 * "true; echo `sleep 1; false` $?"
7255 * should print 1 */ 7298 * should print 1 */
@@ -7427,20 +7470,17 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
7427 return 1; /* "we closed fd" */ 7470 return 1; /* "we closed fd" */
7428 } 7471 }
7429#endif 7472#endif
7473 /* If this one of script's fds? */
7474 if (move_HFILEs_on_redirect(fd, avoid_fd))
7475 return 1; /* yes. "we closed fd" (actually moved it) */
7476
7430 /* Are we called from setup_redirects(squirrel==NULL)? Two cases: 7477 /* Are we called from setup_redirects(squirrel==NULL)? Two cases:
7431 * (1) Redirect in a forked child. No need to save FILEs' fds, 7478 * (1) Redirect in a forked child.
7432 * we aren't going to use them anymore, ok to trash. 7479 * (2) "exec 3>FILE".
7433 * (2) "exec 3>FILE". Bummer. We can save script FILEs' fds,
7434 * but how are we doing to restore them?
7435 * "fileno(fd) = new_fd" can't be done.
7436 */ 7480 */
7437 if (!sqp) 7481 if (!sqp)
7438 return 0; 7482 return 0;
7439 7483
7440 /* If this one of script's fds? */
7441 if (save_FILEs_on_redirect(fd, avoid_fd))
7442 return 1; /* yes. "we closed fd" */
7443
7444 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ 7484 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
7445 *sqp = add_squirrel(*sqp, fd, avoid_fd); 7485 *sqp = add_squirrel(*sqp, fd, avoid_fd);
7446 return 0; /* "we did not close fd" */ 7486 return 0; /* "we did not close fd" */
@@ -7465,8 +7505,6 @@ static void restore_redirects(struct squirrel *sq)
7465 } 7505 }
7466 7506
7467 /* If moved, G.interactive_fd stays on new fd, not restoring it */ 7507 /* If moved, G.interactive_fd stays on new fd, not restoring it */
7468
7469 restore_redirected_FILEs();
7470} 7508}
7471 7509
7472#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU 7510#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
@@ -7474,7 +7512,7 @@ static void close_saved_fds_and_FILE_fds(void)
7474{ 7512{
7475 if (G_interactive_fd) 7513 if (G_interactive_fd)
7476 close(G_interactive_fd); 7514 close(G_interactive_fd);
7477 close_all_FILE_list(); 7515 close_all_HFILE_list();
7478} 7516}
7479#endif 7517#endif
7480 7518
@@ -7487,7 +7525,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq)
7487 return 1; 7525 return 1;
7488#endif 7526#endif
7489 /* If this one of script's fds? */ 7527 /* If this one of script's fds? */
7490 if (fd_in_FILEs(fd)) 7528 if (fd_in_HFILEs(fd))
7491 return 1; 7529 return 1;
7492 7530
7493 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { 7531 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
@@ -7512,7 +7550,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
7512 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); 7550 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
7513 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ 7551 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
7514 * of the heredoc */ 7552 * of the heredoc */
7515 debug_printf_parse("set heredoc '%s'\n", 7553 debug_printf_redir("set heredoc '%s'\n",
7516 redir->rd_filename); 7554 redir->rd_filename);
7517 setup_heredoc(redir); 7555 setup_heredoc(redir);
7518 continue; 7556 continue;
@@ -7524,8 +7562,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
7524 int mode; 7562 int mode;
7525 7563
7526 if (redir->rd_filename == NULL) { 7564 if (redir->rd_filename == NULL) {
7527 /* 7565 /* Examples:
7528 * Examples:
7529 * "cmd >" (no filename) 7566 * "cmd >" (no filename)
7530 * "cmd > <file" (2nd redirect starts too early) 7567 * "cmd > <file" (2nd redirect starts too early)
7531 */ 7568 */
@@ -8706,7 +8743,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
8706 } 8743 }
8707#endif 8744#endif
8708 /* { list } */ 8745 /* { list } */
8709 debug_printf("non-subshell group\n"); 8746 debug_printf_exec("non-subshell group\n");
8710 rcode = 1; /* exitcode if redir failed */ 8747 rcode = 1; /* exitcode if redir failed */
8711 if (setup_redirects(command, &squirrel) == 0) { 8748 if (setup_redirects(command, &squirrel) == 0) {
8712 debug_printf_exec(": run_list\n"); 8749 debug_printf_exec(": run_list\n");
@@ -9871,14 +9908,13 @@ int hush_main(int argc, char **argv)
9871 9908
9872 /* If we are login shell... */ 9909 /* If we are login shell... */
9873 if (flags & OPT_login) { 9910 if (flags & OPT_login) {
9874 FILE *input; 9911 HFILE *input;
9875 debug_printf("sourcing /etc/profile\n"); 9912 debug_printf("sourcing /etc/profile\n");
9876 input = fopen_for_read("/etc/profile"); 9913 input = hfopen("/etc/profile");
9877 if (input != NULL) { 9914 if (input != NULL) {
9878 remember_FILE(input);
9879 install_special_sighandlers(); 9915 install_special_sighandlers();
9880 parse_and_run_file(input); 9916 parse_and_run_file(input);
9881 fclose_and_forget(input); 9917 hfclose(input);
9882 } 9918 }
9883 /* bash: after sourcing /etc/profile, 9919 /* bash: after sourcing /etc/profile,
9884 * tries to source (in the given order): 9920 * tries to source (in the given order):
@@ -9891,7 +9927,7 @@ int hush_main(int argc, char **argv)
9891 9927
9892 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ 9928 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
9893 if (!(flags & OPT_s) && G.global_argv[1]) { 9929 if (!(flags & OPT_s) && G.global_argv[1]) {
9894 FILE *input; 9930 HFILE *input;
9895 /* 9931 /*
9896 * "bash <script>" (which is never interactive (unless -i?)) 9932 * "bash <script>" (which is never interactive (unless -i?))
9897 * sources $BASH_ENV here (without scanning $PATH). 9933 * sources $BASH_ENV here (without scanning $PATH).
@@ -9902,13 +9938,15 @@ int hush_main(int argc, char **argv)
9902 G.global_argv++; 9938 G.global_argv++;
9903 debug_printf("running script '%s'\n", G.global_argv[0]); 9939 debug_printf("running script '%s'\n", G.global_argv[0]);
9904 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */ 9940 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
9905 input = xfopen_for_read(G.global_argv[0]); 9941 input = hfopen(G.global_argv[0]);
9942 if (!input) {
9943 bb_simple_perror_msg_and_die(G.global_argv[0]);
9944 }
9906 xfunc_error_retval = 1; 9945 xfunc_error_retval = 1;
9907 remember_FILE(input);
9908 install_special_sighandlers(); 9946 install_special_sighandlers();
9909 parse_and_run_file(input); 9947 parse_and_run_file(input);
9910#if ENABLE_FEATURE_CLEAN_UP 9948#if ENABLE_FEATURE_CLEAN_UP
9911 fclose_and_forget(input); 9949 hfclose(input);
9912#endif 9950#endif
9913 goto final_return; 9951 goto final_return;
9914 } 9952 }
@@ -10038,7 +10076,7 @@ int hush_main(int argc, char **argv)
10038 ); 10076 );
10039 } 10077 }
10040 10078
10041 parse_and_run_file(stdin); 10079 parse_and_run_file(hfopen(NULL)); /* stdin */
10042 10080
10043 final_return: 10081 final_return:
10044 hush_exit(G.last_exitcode); 10082 hush_exit(G.last_exitcode);
@@ -10849,7 +10887,7 @@ Test that VAR is a valid variable name?
10849static int FAST_FUNC builtin_source(char **argv) 10887static int FAST_FUNC builtin_source(char **argv)
10850{ 10888{
10851 char *arg_path, *filename; 10889 char *arg_path, *filename;
10852 FILE *input; 10890 HFILE *input;
10853 save_arg_t sv; 10891 save_arg_t sv;
10854 char *args_need_save; 10892 char *args_need_save;
10855#if ENABLE_HUSH_FUNCTIONS 10893#if ENABLE_HUSH_FUNCTIONS
@@ -10873,10 +10911,10 @@ static int FAST_FUNC builtin_source(char **argv)
10873 return EXIT_FAILURE; 10911 return EXIT_FAILURE;
10874 } 10912 }
10875 } 10913 }
10876 input = remember_FILE(fopen_or_warn(filename, "r")); 10914 input = hfopen(filename);
10877 free(arg_path); 10915 free(arg_path);
10878 if (!input) { 10916 if (!input) {
10879 /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ 10917 bb_perror_msg("%s", filename);
10880 /* POSIX: non-interactive shell should abort here, 10918 /* POSIX: non-interactive shell should abort here,
10881 * not merely fail. So far no one complained :) 10919 * not merely fail. So far no one complained :)
10882 */ 10920 */
@@ -10895,7 +10933,7 @@ static int FAST_FUNC builtin_source(char **argv)
10895 /* "false; . ./empty_line; echo Zero:$?" should print 0 */ 10933 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
10896 G.last_exitcode = 0; 10934 G.last_exitcode = 0;
10897 parse_and_run_file(input); 10935 parse_and_run_file(input);
10898 fclose_and_forget(input); 10936 hfclose(input);
10899 10937
10900 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ 10938 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
10901 restore_G_args(&sv, argv); 10939 restore_G_args(&sv, argv);
diff --git a/shell/hush_test/hush-heredoc/heredocB.right b/shell/hush_test/hush-heredoc/heredocB.right
new file mode 100644
index 000000000..43ba0b4f9
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredocB.right
@@ -0,0 +1,3 @@
1one - alpha
2two - beta
3three - gamma
diff --git a/shell/hush_test/hush-heredoc/heredocB.tests b/shell/hush_test/hush-heredoc/heredocB.tests
new file mode 100755
index 000000000..45ea4687f
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredocB.tests
@@ -0,0 +1,12 @@
1while read line1; do
2 read line2 <&3
3 echo $line1 - $line2
4done <<EOF1 3<<EOF2
5one
6two
7three
8EOF1
9alpha
10beta
11gamma
12EOF2
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests
index 740daa461..a8d93ce4f 100755
--- a/shell/hush_test/hush-redir/redir_script.tests
+++ b/shell/hush_test/hush-redir/redir_script.tests
@@ -27,6 +27,10 @@ test x"$fds1" = x"$fds" \
27test x"$fds1" = x" 10>&- 3>&-" && \ 27test x"$fds1" = x" 10>&- 3>&-" && \
28test x"$fds" = x" 11>&- 3>&-" \ 28test x"$fds" = x" 11>&- 3>&-" \
29&& { echo "Ok: script fd is not closed"; exit 0; } 29&& { echo "Ok: script fd is not closed"; exit 0; }
30# or we see that fd 3 moved to fd 10:
31test x"$fds1" = x" 3>&- 4>&-" && \
32test x"$fds" = x" 10>&- 3>&-" \
33&& { echo "Ok: script fd is not closed"; exit 0; }
30 34
31echo "Bug: script fd is closed" 35echo "Bug: script fd is closed"
32echo "fds1:$fds1" 36echo "fds1:$fds1"