Blame lib/open.c

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