Blame lib/open.c

Packit Service fdd496
/* Open a descriptor to a file.
Packit Service fdd496
   Copyright (C) 2007-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 Bruno Haible <bruno@clisp.org>, 2007.  */
Packit Service fdd496
Packit Service fdd496
/* If the user's config.h happens to include <fcntl.h>, let it include only
Packit Service fdd496
   the system's <fcntl.h> here, so that orig_open doesn't recurse to
Packit Service fdd496
   rpl_open.  */
Packit Service fdd496
#define __need_system_fcntl_h
Packit Service fdd496
#include <config.h>
Packit Service fdd496
Packit Service fdd496
/* Get the original definition of open.  It might be defined as a macro.  */
Packit Service fdd496
#include <fcntl.h>
Packit Service fdd496
#include <sys/types.h>
Packit Service fdd496
#undef __need_system_fcntl_h
Packit Service fdd496
Packit Service fdd496
static int
Packit Service fdd496
orig_open (const char *filename, int flags, mode_t mode)
Packit Service fdd496
{
Packit Service fdd496
  return open (filename, flags, mode);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Specification.  */
Packit Service fdd496
/* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates
Packit Service fdd496
   this include because of the preliminary #include <fcntl.h> above.  */
Packit Service fdd496
#include "fcntl.h"
Packit Service fdd496
Packit Service fdd496
#include <errno.h>
Packit Service fdd496
#include <stdarg.h>
Packit Service fdd496
#include <string.h>
Packit Service fdd496
#include <sys/types.h>
Packit Service fdd496
#include <sys/stat.h>
Packit Service fdd496
#include <unistd.h>
Packit Service fdd496
Packit Service fdd496
#ifndef REPLACE_OPEN_DIRECTORY
Packit Service fdd496
# define REPLACE_OPEN_DIRECTORY 0
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
open (const char *filename, int flags, ...)
Packit Service fdd496
{
Packit Service fdd496
  mode_t mode;
Packit Service fdd496
  int fd;
Packit Service fdd496
Packit Service fdd496
  mode = 0;
Packit Service fdd496
  if (flags & O_CREAT)
Packit Service fdd496
    {
Packit Service fdd496
      va_list arg;
Packit Service fdd496
      va_start (arg, flags);
Packit Service fdd496
Packit Service fdd496
      /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
Packit Service fdd496
         creates crashing code when 'mode_t' is smaller than 'int'.  */
Packit Service fdd496
      mode = va_arg (arg, PROMOTED_MODE_T);
Packit Service fdd496
Packit Service fdd496
      va_end (arg);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
#if GNULIB_defined_O_NONBLOCK
Packit Service fdd496
  /* The only known platform that lacks O_NONBLOCK is mingw, but it
Packit Service fdd496
     also lacks named pipes and Unix sockets, which are the only two
Packit Service fdd496
     file types that require non-blocking handling in open().
Packit Service fdd496
     Therefore, it is safe to ignore O_NONBLOCK here.  It is handy
Packit Service fdd496
     that mingw also lacks openat(), so that is also covered here.  */
Packit Service fdd496
  flags &= ~O_NONBLOCK;
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit Service fdd496
  if (strcmp (filename, "/dev/null") == 0)
Packit Service fdd496
    filename = "NUL";
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#if OPEN_TRAILING_SLASH_BUG
Packit Service fdd496
  /* If the filename ends in a slash and one of O_CREAT, O_WRONLY, O_RDWR
Packit Service fdd496
     is specified, then fail.
Packit Service fdd496
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
Packit Service fdd496
     says that
Packit Service fdd496
       "A pathname that contains at least one non-slash character and that
Packit Service fdd496
        ends with one or more trailing slashes shall be resolved as if a
Packit Service fdd496
        single dot character ( '.' ) were appended to the pathname."
Packit Service fdd496
     and
Packit Service fdd496
       "The special filename dot shall refer to the directory specified by
Packit Service fdd496
        its predecessor."
Packit Service fdd496
     If the named file already exists as a directory, then
Packit Service fdd496
       - if O_CREAT is specified, open() must fail because of the semantics
Packit Service fdd496
         of O_CREAT,
Packit Service fdd496
       - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
Packit Service fdd496
         <http://www.opengroup.org/susv3/functions/open.html> says that it
Packit Service fdd496
         fails with errno = EISDIR in this case.
Packit Service fdd496
     If the named file does not exist or does not name a directory, then
Packit Service fdd496
       - if O_CREAT is specified, open() must fail since open() cannot create
Packit Service fdd496
         directories,
Packit Service fdd496
       - if O_WRONLY or O_RDWR is specified, open() must fail because the
Packit Service fdd496
         file does not contain a '.' directory.  */
Packit Service fdd496
  if (flags & (O_CREAT | O_WRONLY | O_RDWR))
Packit Service fdd496
    {
Packit Service fdd496
      size_t len = strlen (filename);
Packit Service fdd496
      if (len > 0 && filename[len - 1] == '/')
Packit Service fdd496
        {
Packit Service fdd496
          errno = EISDIR;
Packit Service fdd496
          return -1;
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  fd = orig_open (filename, flags, mode);
Packit Service fdd496
Packit Service fdd496
#if REPLACE_FCHDIR
Packit Service fdd496
  /* Implementing fchdir and fdopendir requires the ability to open a
Packit Service fdd496
     directory file descriptor.  If open doesn't support that (as on
Packit Service fdd496
     mingw), we use a dummy file that behaves the same as directories
Packit Service fdd496
     on Linux (ie. always reports EOF on attempts to read()), and
Packit Service fdd496
     override fstat() in fchdir.c to hide the fact that we have a
Packit Service fdd496
     dummy.  */
Packit Service fdd496
  if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES
Packit Service fdd496
      && ((flags & O_ACCMODE) == O_RDONLY
Packit Service fdd496
          || (O_SEARCH != O_RDONLY && (flags & O_ACCMODE) == O_SEARCH)))
Packit Service fdd496
    {
Packit Service fdd496
      struct stat statbuf;
Packit Service fdd496
      if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
Packit Service fdd496
        {
Packit Service fdd496
          /* Maximum recursion depth of 1.  */
Packit Service fdd496
          fd = open ("/dev/null", flags, mode);
Packit Service fdd496
          if (0 <= fd)
Packit Service fdd496
            fd = _gl_register_fd (fd, filename);
Packit Service fdd496
        }
Packit Service fdd496
      else
Packit Service fdd496
        errno = EACCES;
Packit Service fdd496
    }
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#if OPEN_TRAILING_SLASH_BUG
Packit Service fdd496
  /* If the filename ends in a slash and fd does not refer to a directory,
Packit Service fdd496
     then fail.
Packit Service fdd496
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
Packit Service fdd496
     says that
Packit Service fdd496
       "A pathname that contains at least one non-slash character and that
Packit Service fdd496
        ends with one or more trailing slashes shall be resolved as if a
Packit Service fdd496
        single dot character ( '.' ) were appended to the pathname."
Packit Service fdd496
     and
Packit Service fdd496
       "The special filename dot shall refer to the directory specified by
Packit Service fdd496
        its predecessor."
Packit Service fdd496
     If the named file without the slash is not a directory, open() must fail
Packit Service fdd496
     with ENOTDIR.  */
Packit Service fdd496
  if (fd >= 0)
Packit Service fdd496
    {
Packit Service fdd496
      /* We know len is positive, since open did not fail with ENOENT.  */
Packit Service fdd496
      size_t len = strlen (filename);
Packit Service fdd496
      if (filename[len - 1] == '/')
Packit Service fdd496
        {
Packit Service fdd496
          struct stat statbuf;
Packit Service fdd496
Packit Service fdd496
          if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
Packit Service fdd496
            {
Packit Service fdd496
              close (fd);
Packit Service fdd496
              errno = ENOTDIR;
Packit Service fdd496
              return -1;
Packit Service fdd496
            }
Packit Service fdd496
        }
Packit Service fdd496
    }
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
#if REPLACE_FCHDIR
Packit Service fdd496
  if (!REPLACE_OPEN_DIRECTORY && 0 <= fd)
Packit Service fdd496
    fd = _gl_register_fd (fd, filename);
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  return fd;
Packit Service fdd496
}