Blame lib/freopen-safer.c

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