Blame lib/freopen-safer.c

Packit 33f14e
/* Invoke freopen, but avoid some glitches.
Packit 33f14e
Packit 33f14e
   Copyright (C) 2009-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
/* Written by Eric Blake.  */
Packit 33f14e
Packit 33f14e
#include <config.h>
Packit 33f14e
Packit 33f14e
#include "stdio-safer.h"
Packit 33f14e
Packit 33f14e
#include <errno.h>
Packit 33f14e
#include <fcntl.h>
Packit 33f14e
#include <stdbool.h>
Packit 33f14e
#include <unistd.h>
Packit 33f14e
Packit 33f14e
#ifndef FALLTHROUGH
Packit 33f14e
# if __GNUC__ < 7
Packit 33f14e
#  define FALLTHROUGH ((void) 0)
Packit 33f14e
# else
Packit 33f14e
#  define FALLTHROUGH __attribute__ ((__fallthrough__))
Packit 33f14e
# endif
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* Guarantee that FD is open; all smaller FDs must already be open.
Packit 33f14e
   Return true if successful.  */
Packit 33f14e
static bool
Packit 33f14e
protect_fd (int fd)
Packit 33f14e
{
Packit 33f14e
  int value = open ("/dev/null", O_RDONLY);
Packit 33f14e
  if (value != fd)
Packit 33f14e
    {
Packit 33f14e
      if (0 <= value)
Packit 33f14e
        {
Packit 33f14e
          close (value);
Packit 33f14e
          errno = EBADF; /* Unexpected; this is as good as anything else.  */
Packit 33f14e
        }
Packit 33f14e
      return false;
Packit 33f14e
    }
Packit 33f14e
  return true;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Like freopen, but guarantee that reopening stdin, stdout, or stderr
Packit 33f14e
   preserves the invariant that STDxxx_FILENO==fileno(stdxxx), and
Packit 33f14e
   that no other stream will interfere with the standard streams.
Packit 33f14e
   This is necessary because most freopen implementations will change
Packit 33f14e
   the associated fd of a stream to the lowest available slot.  */
Packit 33f14e
Packit 33f14e
FILE *
Packit 33f14e
freopen_safer (char const *name, char const *mode, FILE *f)
Packit 33f14e
{
Packit 33f14e
  /* Unfortunately, we cannot use the fopen_safer approach of using
Packit 33f14e
     fdopen (dup_safer (fileno (freopen (cmd, mode, f)))), because we
Packit 33f14e
     need to return f itself.  The implementation of freopen(NULL,m,f)
Packit 33f14e
     is system-dependent, so the best we can do is guarantee that all
Packit 33f14e
     lower-valued standard fds are open prior to the freopen call,
Packit 33f14e
     even though this puts more pressure on open fds.  */
Packit 33f14e
  bool protect_in = false;
Packit 33f14e
  bool protect_out = false;
Packit 33f14e
  bool protect_err = false;
Packit 33f14e
  int saved_errno;
Packit 33f14e
Packit 33f14e
  switch (fileno (f))
Packit 33f14e
    {
Packit 33f14e
    default: /* -1 or not a standard stream.  */
Packit 33f14e
      if (dup2 (STDERR_FILENO, STDERR_FILENO) != STDERR_FILENO)
Packit 33f14e
        protect_err = true;
Packit 33f14e
      FALLTHROUGH;
Packit 33f14e
    case STDERR_FILENO:
Packit 33f14e
      if (dup2 (STDOUT_FILENO, STDOUT_FILENO) != STDOUT_FILENO)
Packit 33f14e
        protect_out = true;
Packit 33f14e
      FALLTHROUGH;
Packit 33f14e
    case STDOUT_FILENO:
Packit 33f14e
      if (dup2 (STDIN_FILENO, STDIN_FILENO) != STDIN_FILENO)
Packit 33f14e
        protect_in = true;
Packit 33f14e
      FALLTHROUGH;
Packit 33f14e
    case STDIN_FILENO:
Packit 33f14e
      /* Nothing left to protect.  */
Packit 33f14e
      break;
Packit 33f14e
    }
Packit 33f14e
  if (protect_in && !protect_fd (STDIN_FILENO))
Packit 33f14e
    f = NULL;
Packit 33f14e
  else if (protect_out && !protect_fd (STDOUT_FILENO))
Packit 33f14e
    f = NULL;
Packit 33f14e
  else if (protect_err && !protect_fd (STDERR_FILENO))
Packit 33f14e
    f = NULL;
Packit 33f14e
  else
Packit 33f14e
    f = freopen (name, mode, f);
Packit 33f14e
  saved_errno = errno;
Packit 33f14e
  if (protect_err)
Packit 33f14e
    close (STDERR_FILENO);
Packit 33f14e
  if (protect_out)
Packit 33f14e
    close (STDOUT_FILENO);
Packit 33f14e
  if (protect_in)
Packit 33f14e
    close (STDIN_FILENO);
Packit 33f14e
  if (!f)
Packit 33f14e
    errno = saved_errno;
Packit 33f14e
  return f;
Packit 33f14e
}