Blame lib/open.c

Packit Service a2489d
/* Open a descriptor to a file.
Packit Service a2489d
   Copyright (C) 2007-2018 Free Software Foundation, Inc.
Packit Service a2489d
Packit Service a2489d
   This program is free software: you can redistribute it and/or modify
Packit Service a2489d
   it under the terms of the GNU General Public License as published by
Packit Service a2489d
   the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
   (at your option) any later version.
Packit Service a2489d
Packit Service a2489d
   This program is distributed in the hope that it will be useful,
Packit Service a2489d
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
   GNU General Public License for more details.
Packit Service a2489d
Packit Service a2489d
   You should have received a copy of the GNU General Public License
Packit Service a2489d
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit Service a2489d
Packit Service a2489d
/* Written by Bruno Haible <bruno@clisp.org>, 2007.  */
Packit Service a2489d
Packit Service a2489d
/* If the user's config.h happens to include <fcntl.h>, let it include only
Packit Service a2489d
   the system's <fcntl.h> here, so that orig_open doesn't recurse to
Packit Service a2489d
   rpl_open.  */
Packit Service a2489d
#define __need_system_fcntl_h
Packit Service a2489d
#include <config.h>
Packit Service a2489d
Packit Service a2489d
/* Get the original definition of open.  It might be defined as a macro.  */
Packit Service a2489d
#include <fcntl.h>
Packit Service a2489d
#include <sys/types.h>
Packit Service a2489d
#undef __need_system_fcntl_h
Packit Service a2489d
Packit Service a2489d
static int
Packit Service a2489d
orig_open (const char *filename, int flags, mode_t mode)
Packit Service a2489d
{
Packit Service a2489d
  return open (filename, flags, mode);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* Specification.  */
Packit Service a2489d
/* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates
Packit Service a2489d
   this include because of the preliminary #include <fcntl.h> above.  */
Packit Service a2489d
#include "fcntl.h"
Packit Service a2489d
Packit Service a2489d
#include "cloexec.h"
Packit Service a2489d
Packit Service a2489d
#include <errno.h>
Packit Service a2489d
#include <stdarg.h>
Packit Service a2489d
#include <string.h>
Packit Service a2489d
#include <sys/types.h>
Packit Service a2489d
#include <sys/stat.h>
Packit Service a2489d
#include <unistd.h>
Packit Service a2489d
Packit Service a2489d
#ifndef REPLACE_OPEN_DIRECTORY
Packit Service a2489d
# define REPLACE_OPEN_DIRECTORY 0
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
int
Packit Service a2489d
open (const char *filename, int flags, ...)
Packit Service a2489d
{
Packit Service a2489d
  /* 0 = unknown, 1 = yes, -1 = no.  */
Packit Service a2489d
#if GNULIB_defined_O_CLOEXEC
Packit Service a2489d
  int have_cloexec = -1;
Packit Service a2489d
#else
Packit Service a2489d
  static int have_cloexec;
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
  mode_t mode;
Packit Service a2489d
  int fd;
Packit Service a2489d
Packit Service a2489d
  mode = 0;
Packit Service a2489d
  if (flags & O_CREAT)
Packit Service a2489d
    {
Packit Service a2489d
      va_list arg;
Packit Service a2489d
      va_start (arg, flags);
Packit Service a2489d
Packit Service a2489d
      /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
Packit Service a2489d
         creates crashing code when 'mode_t' is smaller than 'int'.  */
Packit Service a2489d
      mode = va_arg (arg, PROMOTED_MODE_T);
Packit Service a2489d
Packit Service a2489d
      va_end (arg);
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
#if GNULIB_defined_O_NONBLOCK
Packit Service a2489d
  /* The only known platform that lacks O_NONBLOCK is mingw, but it
Packit Service a2489d
     also lacks named pipes and Unix sockets, which are the only two
Packit Service a2489d
     file types that require non-blocking handling in open().
Packit Service a2489d
     Therefore, it is safe to ignore O_NONBLOCK here.  It is handy
Packit Service a2489d
     that mingw also lacks openat(), so that is also covered here.  */
Packit Service a2489d
  flags &= ~O_NONBLOCK;
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
#if defined _WIN32 && ! defined __CYGWIN__
Packit Service a2489d
  if (strcmp (filename, "/dev/null") == 0)
Packit Service a2489d
    filename = "NUL";
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
#if OPEN_TRAILING_SLASH_BUG
Packit Service a2489d
  /* If the filename ends in a slash and one of O_CREAT, O_WRONLY, O_RDWR
Packit Service a2489d
     is specified, then fail.
Packit Service a2489d
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
Packit Service a2489d
     says that
Packit Service a2489d
       "A pathname that contains at least one non-slash character and that
Packit Service a2489d
        ends with one or more trailing slashes shall be resolved as if a
Packit Service a2489d
        single dot character ( '.' ) were appended to the pathname."
Packit Service a2489d
     and
Packit Service a2489d
       "The special filename dot shall refer to the directory specified by
Packit Service a2489d
        its predecessor."
Packit Service a2489d
     If the named file already exists as a directory, then
Packit Service a2489d
       - if O_CREAT is specified, open() must fail because of the semantics
Packit Service a2489d
         of O_CREAT,
Packit Service a2489d
       - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
Packit Service a2489d
         <http://www.opengroup.org/susv3/functions/open.html> says that it
Packit Service a2489d
         fails with errno = EISDIR in this case.
Packit Service a2489d
     If the named file does not exist or does not name a directory, then
Packit Service a2489d
       - if O_CREAT is specified, open() must fail since open() cannot create
Packit Service a2489d
         directories,
Packit Service a2489d
       - if O_WRONLY or O_RDWR is specified, open() must fail because the
Packit Service a2489d
         file does not contain a '.' directory.  */
Packit Service a2489d
  if (flags & (O_CREAT | O_WRONLY | O_RDWR))
Packit Service a2489d
    {
Packit Service a2489d
      size_t len = strlen (filename);
Packit Service a2489d
      if (len > 0 && filename[len - 1] == '/')
Packit Service a2489d
        {
Packit Service a2489d
          errno = EISDIR;
Packit Service a2489d
          return -1;
Packit Service a2489d
        }
Packit Service a2489d
    }
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
  fd = orig_open (filename,
Packit Service a2489d
                  flags & ~(have_cloexec <= 0 ? O_CLOEXEC : 0), mode);
Packit Service a2489d
Packit Service a2489d
  if (flags & O_CLOEXEC)
Packit Service a2489d
    {
Packit Service a2489d
      if (! have_cloexec)
Packit Service a2489d
        {
Packit Service a2489d
          if (0 <= fd)
Packit Service a2489d
            have_cloexec = 1;
Packit Service a2489d
          else if (errno == EINVAL)
Packit Service a2489d
            {
Packit Service a2489d
              fd = orig_open (filename, flags & ~O_CLOEXEC, mode);
Packit Service a2489d
              have_cloexec = -1;
Packit Service a2489d
            }
Packit Service a2489d
        }
Packit Service a2489d
      if (have_cloexec < 0 && 0 <= fd)
Packit Service a2489d
        set_cloexec_flag (fd, true);
Packit Service a2489d
    }
Packit Service a2489d
Packit Service a2489d
Packit Service a2489d
#if REPLACE_FCHDIR
Packit Service a2489d
  /* Implementing fchdir and fdopendir requires the ability to open a
Packit Service a2489d
     directory file descriptor.  If open doesn't support that (as on
Packit Service a2489d
     mingw), we use a dummy file that behaves the same as directories
Packit Service a2489d
     on Linux (ie. always reports EOF on attempts to read()), and
Packit Service a2489d
     override fstat() in fchdir.c to hide the fact that we have a
Packit Service a2489d
     dummy.  */
Packit Service a2489d
  if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES
Packit Service a2489d
      && ((flags & O_ACCMODE) == O_RDONLY
Packit Service a2489d
          || (O_SEARCH != O_RDONLY && (flags & O_ACCMODE) == O_SEARCH)))
Packit Service a2489d
    {
Packit Service a2489d
      struct stat statbuf;
Packit Service a2489d
      if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
Packit Service a2489d
        {
Packit Service a2489d
          /* Maximum recursion depth of 1.  */
Packit Service a2489d
          fd = open ("/dev/null", flags, mode);
Packit Service a2489d
          if (0 <= fd)
Packit Service a2489d
            fd = _gl_register_fd (fd, filename);
Packit Service a2489d
        }
Packit Service a2489d
      else
Packit Service a2489d
        errno = EACCES;
Packit Service a2489d
    }
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
#if OPEN_TRAILING_SLASH_BUG
Packit Service a2489d
  /* If the filename ends in a slash and fd does not refer to a directory,
Packit Service a2489d
     then fail.
Packit Service a2489d
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
Packit Service a2489d
     says that
Packit Service a2489d
       "A pathname that contains at least one non-slash character and that
Packit Service a2489d
        ends with one or more trailing slashes shall be resolved as if a
Packit Service a2489d
        single dot character ( '.' ) were appended to the pathname."
Packit Service a2489d
     and
Packit Service a2489d
       "The special filename dot shall refer to the directory specified by
Packit Service a2489d
        its predecessor."
Packit Service a2489d
     If the named file without the slash is not a directory, open() must fail
Packit Service a2489d
     with ENOTDIR.  */
Packit Service a2489d
  if (fd >= 0)
Packit Service a2489d
    {
Packit Service a2489d
      /* We know len is positive, since open did not fail with ENOENT.  */
Packit Service a2489d
      size_t len = strlen (filename);
Packit Service a2489d
      if (filename[len - 1] == '/')
Packit Service a2489d
        {
Packit Service a2489d
          struct stat statbuf;
Packit Service a2489d
Packit Service a2489d
          if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
Packit Service a2489d
            {
Packit Service a2489d
              close (fd);
Packit Service a2489d
              errno = ENOTDIR;
Packit Service a2489d
              return -1;
Packit Service a2489d
            }
Packit Service a2489d
        }
Packit Service a2489d
    }
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
#if REPLACE_FCHDIR
Packit Service a2489d
  if (!REPLACE_OPEN_DIRECTORY && 0 <= fd)
Packit Service a2489d
    fd = _gl_register_fd (fd, filename);
Packit Service a2489d
#endif
Packit Service a2489d
Packit Service a2489d
  return fd;
Packit Service a2489d
}