ash: fix redirection of fd 0 in scripts are sourced from interactive ash
authorDenys Vlasenko <vda.linux@googlemail.com>
Thu, 3 Jun 2010 02:28:28 +0000 (04:28 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 3 Jun 2010 02:28:28 +0000 (04:28 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/ash_test/ash-signals/signal5.right
shell/ash_test/ash-signals/signal5.tests

index 08ad0f451649529107c6d3af88a12a38f26c881a..067feb13d058cda4c3526cac2366cf75fd92d69a 100644 (file)
@@ -1070,7 +1070,7 @@ ash_vmsg(const char *msg, va_list ap)
        if (commandname) {
                if (strcmp(arg0, commandname))
                        fprintf(stderr, "%s: ", commandname);
-               if (!iflag || g_parsefile->fd)
+               if (!iflag || g_parsefile->fd > 0)
                        fprintf(stderr, "line %d: ", startlinno);
        }
        vfprintf(stderr, msg, ap);
@@ -5063,15 +5063,26 @@ static int is_hidden_fd(struct redirtab *rp, int fd)
 
        if (fd == -1)
                return 0;
+       /* Check open scripts' fds */
        pf = g_parsefile;
        while (pf) {
-               if (fd == pf->fd) {
+               /* We skip fd == 0 case because of the following case:
+                * $ ash  # running ash interactively
+                * $ . ./script.sh
+                * and in script.sh: "exec 9>&0".
+                * Even though top-level fd _is_ 0,
+                * it's still ok to use it: "read" builtin uses it,
+                * why should we cripple "exec" builtin?
+                */
+               if (pf->fd > 0 && fd == pf->fd) {
                        return 1;
                }
                pf = pf->prev;
        }
+
        if (!rp)
                return 0;
+       /* Check saved fds of redirects */
        fd |= COPYFD_RESTORE;
        for (i = 0; i < rp->pair_count; i++) {
                if (rp->two_fd[i].copy == fd) {
@@ -5084,9 +5095,7 @@ static int is_hidden_fd(struct redirtab *rp, int fd)
 /*
  * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
  * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout, is saved in memory.
+ * undone by calling popredir.
  */
 /* flags passed to redirect */
 #define REDIR_PUSH    01        /* save previous values of file descriptors */
@@ -5132,13 +5141,15 @@ redirect(union node *redir, int flags)
        }
 
        do {
+               int right_fd = -1;
                fd = redir->nfile.fd;
                if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
-                       int right_fd = redir->ndup.dupfd;
+                       right_fd = redir->ndup.dupfd;
+                       //bb_error_msg("doing %d > %d", fd, right_fd);
                        /* redirect from/to same file descriptor? */
                        if (right_fd == fd)
                                continue;
-                       /* echo >&10 and 10 is a fd opened to the sh script? */
+                       /* "echo >&10" and 10 is a fd opened to a sh script? */
                        if (is_hidden_fd(sv, right_fd)) {
                                errno = EBADF; /* as if it is closed */
                                ash_msg_and_raise_error("%d: %m", right_fd);
@@ -5160,7 +5171,10 @@ redirect(union node *redir, int flags)
 #endif
                if (need_to_remember(sv, fd)) {
                        /* Copy old descriptor */
-                       i = fcntl(fd, F_DUPFD, 10);
+                       /* Careful to not accidentally "save"
+                        * to the same fd as right side fd in N>&M */
+                       int minfd = right_fd < 10 ? 10 : right_fd + 1;
+                       i = fcntl(fd, F_DUPFD, minfd);
 /* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
  * are closed in popredir() in the child, preventing them from leaking
  * into child. (popredir() also cleans up the mess in case of failures)
@@ -12994,8 +13008,9 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                 * Ensure we don't falsely claim that 0 (stdin)
                 * is one of stacked source fds.
                 * Testcase: ash -c 'exec 1>&0' must not complain. */
-               if (!sflag)
-                       g_parsefile->fd = -1;
+               // if (!sflag) g_parsefile->fd = -1;
+               // ^^ not necessary since now we special-case fd 0
+               // in is_hidden_fd() to not be considered "hidden fd"
                evalstring(minusc, 0);
        }
 
index 162f56bbcf9defc2c03089d96b141dfcbf8142ba..7cfd4110e6885ee2ccbd677c3dc8a1f128e5a5b5 100644 (file)
@@ -1,5 +1,5 @@
-sleeping for 3 sec
-sleeping for 2 sec
+Sleeping
+Sleeping
 Waiting
 2 sec passed, sending USR1 to parent
 USR1 received
index 371120e9525592bfb2ebdb7b660a30d5827e2a12..179bcdd8097a60058000c8157a4dbff7e29d598a 100755 (executable)
@@ -1,6 +1,6 @@
 trap "echo USR1 received" USR1
 stub() {
-    echo "sleeping for $1 sec"
+    echo "Sleeping"
     sleep $1
     echo "$1 sec passed, sending USR1 to parent"
     kill -USR1 $$