Date:        Sat, 28 Apr 2018 03:53:12 +0200
    From:        Martijn Dekker <mart...@inlv.org>
    Message-ID:  <81b93245-e42f-ad62-4005-8ad676733...@inlv.org>

  |  How does NetBSD sh handle this?

This isn't really the best place for code samples, but ...

"fd" is the file descriptor in question:

                if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) {
                        INTOFF;
                        if (big_sh_fd < 10)
                                find_big_fd();
                        if ((i = fcntl(fd, F_DUPFD, big_sh_fd)) == -1) {
                                switch (errno) {
                                case EBADF:
                                        i = CLOSED;
                                        break;
                                case EMFILE:
                                case EINVAL:
                                        find_big_fd();
                                        i = fcntl(fd, F_DUPFD, big_sh_fd);
                                        if (i >= 0)
                                                break;
                                        /* FALLTHRU */
                                default:
                                        i = errno;
                                        INTON;    /* XXX not needed here ? */
                                        error("%d: %s", fd, strerror(i));
                                        /* NOTREACHED */
                                }
                        }
                        if (i >= 0)
                                (void)fcntl(i, F_SETFD, FD_CLOEXEC);
                        fd_rename(sv, fd, i);

CLOSED is < 0 

fd_rename() does (aside from bookkeeping)

        rl->orig = from;
        rl->into = to;

The "sv" arg is the data struct where all this is
saved (rl is alloc'd memory, linked to it).

and then later, when things are being restored

                        if (rl->into < 0)
                                close(rl->orig);
                        else
                                movefd(rl->into, rl->orig);

movefd() ends up translating into dup2() with
error, and close-on-exec handling.

The FreeBSD sh is similar, but simpler - they don't deal
with user fd's >= 10, which makes the data structs needed
simpler, and much easier to avoid user fd's stepping all
over the shell's internal fds - we allow user fds to be anything
the system allows, and the shell makes sure it moves its
own fds around if needed to avoid issues.

The EINVAL and EMFILE handling is dealing with the
consequences of the user/script playing with ulimit, or
at least as much as is possible.

  | If it's a bug, surely it would meet that requirement.

Not for me to say, but it it doesn't have to be different
(rather than just would be nicer if it was different) then
it can be hard to justify making changes.

kre

Reply via email to