view lib/open.c @ 12766:2c14f1e449e5

warn-on-use: use instead of link-warning Each *.in.h file serves two purposes - provide enough self-contained content to serve as a replacement for the system header regardless of the user's compiler, and offer the developer the ability to detect any gnulib modules that might have been inadvertantly excluded. The former requires independence from config.h, and routes everything through a Makefile.am snippet that uses @@ substitution specific to the modules that were in use, with details learned at configure time. The latter works by poisoning anything that gnulib did not replace, but which the developer had natively available, explaining why their tests passed during development. Poisoning relies on gcc features, and requires manual triggering by adding -DGNULIB_POSIXCHECK to CFLAGS; it assumes that <config.h> is properly included. In fact, we do not want to use @@ substitution for HAVE_DECL_* during poisoning, because the warning is only relevant for the gnulib modules which were not included, and thus where the m4 macros to set proper @@ values have not been run. Furthermore, we only need to poison interfaces that already have a declaration; if something is not declared, then the developer wouldn't have been able to link, so their code won't be using the problematic interface in the first place. * modules/stdio (Depends-on, Makefile.am): Drop link-warning. * modules/unistd (Depends-on, Makefile.am): Likewise. * modules/arpa_inet (Depends-on): Replace link-warning with warn-on-use. (Makefile.am): Update rules accordingly. * modules/ctype (Depends-on, Makefile.am): Likewise. * modules/dirent (Depends-on, Makefile.am): Likewise. * modules/fcntl-h (Depends-on, Makefile.am): Likewise. * modules/inttypes (Depends-on, Makefile.am): Likewise. * modules/langinfo (Depends-on, Makefile.am): Likewise. * modules/locale (Depends-on, Makefile.am): Likewise. * modules/math (Depends-on, Makefile.am): Likewise. * modules/search (Depends-on, Makefile.am): Likewise. * modules/signal (Depends-on, Makefile.am): Likewise. * modules/spawn (Depends-on, Makefile.am): Likewise. * modules/stdlib (Depends-on, Makefile.am): Likewise. * modules/string (Depends-on, Makefile.am): Likewise. * modules/strings (Depends-on, Makefile.am): Likewise. * modules/sys_file (Depends-on, Makefile.am): Likewise. * modules/sys_ioctl (Depends-on, Makefile.am): Likewise. * modules/sys_select (Depends-on, Makefile.am): Likewise. * modules/sys_socket (Depends-on, Makefile.am): Likewise. * modules/sys_stat (Depends-on, Makefile.am): Likewise. * modules/sys_times (Depends-on, Makefile.am): Likewise. * modules/sys_utsname (Depends-on, Makefile.am): Likewise. * modules/wchar (Depends-on, Makefile.am): Likewise. * m4/arpa_inet_h.m4 (gl_HEADER_ARPA_INET): Check which functions should be poisoned. * m4/ctype.m4 (gl_CTYPE_H): Likewise. * m4/dirent_h.m4 (gl_DIRENT_H): Likewise. * m4/fcntl_h.m4 (gl_FCNTL_H): Likewise. * m4/inttypes.m4 (gl_INTTYPES_H): Likewise. * m4/langinfo_h.m4 (gl_LANGINFO_H): Likewise. * m4/locale_h.m4 (gl_LOCALE_H): Likewise. * m4/math_h.m4 (gl_MATH_H): Likewise. * m4/search_h.m4 (gl_SEARCH_H): Likewise. * m4/signal_h.m4 (gl_SIGNAL_H): Likewise. * m4/spawn_h.m4 (gl_SPAWN_H): Likewise. * m4/stdio_h.m4 (gl_STDIO_H): Likewise. * m4/stdlib_h.m4 (gl_STDLIB_H): Likewise. * m4/string_h.m4 (gl_HEADER_STRING_H_BODY): Likewise. * m4/strings_h.m4 (gl_HEADER_STRINGS_H_BODY): Likewise. * m4/sys_file_h.m4 (gl_HEADER_SYS_FILE_H_DEFAULTS): Likewise. * m4/sys_ioctl_h.m4 (gl_SYS_IOCTL_H): Likewise. * m4/sys_select_h.m4 (gl_HEADER_SYS_SELECT): Likewise. * m4/sys_socket_h.m4 (gl_HEADER_SYS_SOCKET): Likewise. * m4/sys_stat_h.m4 (gl_HEADER_SYS_STAT_H): Likewise. * m4/sys_times_h.m4 (gl_SYS_TIMES_H): Likewise. * m4/sys_utsname_h.m4 (gl_SYS_UTSNAME_H): Likewise. * m4/unistd_h.m4 (gl_UNISTD_H): Likewise. * m4/wchar.m4 (gl_WCHAR_H): Likewise. * lib/arpa_inet.in.h: Use _GL_WARN_ON_USE instead of GL_LINK_WARNING. * lib/ctype.in.h: Likewise. * lib/dirent.in.h: Likewise. * lib/fcntl.in.h: Likewise. * lib/inttypes.in.h: Likewise. * lib/langinfo.in.h: Likewise. * lib/locale.in.h: Likewise. * lib/math.in.h: Likewise. * lib/search.in.h: Likewise. * lib/signal.in.h: Likewise. * lib/spawn.in.h: Likewise. * lib/stdio.in.h: Likewise. * lib/stdlib.in.h: Likewise. * lib/string.in.h: Likewise. * lib/strings.in.h: Likewise. * lib/sys_file.in.h: Likewise. * lib/sys_ioctl.in.h: Likewise. * lib/sys_select.in.h: Likewise. * lib/sys_socket.in.h: Likewise. * lib/sys_stat.in.h: Likewise. * lib/sys_times.in.h: Likewise. * lib/sys_utsname.in.h: Likewise. * lib/unistd.in.h: Likewise. * lib/wchar.in.h: Likewise. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Wed, 16 Dec 2009 16:20:28 -0700 (2009-12-16)
parents c2cbabec01dd
children 241057e2e60f
line wrap: on
line source
/* Open a descriptor to a file.
   Copyright (C) 2007-2010 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Written by Bruno Haible <bruno@clisp.org>, 2007.  */

#include <config.h>

/* Get the original definition of open.  It might be defined as a macro.  */
#define __need_system_fcntl_h
#include <fcntl.h>
#undef __need_system_fcntl_h
#include <sys/types.h>

static inline int
orig_open (const char *filename, int flags, mode_t mode)
{
  return open (filename, flags, mode);
}

/* Specification.  */
#include <fcntl.h>

#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#ifndef REPLACE_OPEN_DIRECTORY
# define REPLACE_OPEN_DIRECTORY 0
#endif

int
open (const char *filename, int flags, ...)
{
  mode_t mode;
  int fd;

  mode = 0;
  if (flags & O_CREAT)
    {
      va_list arg;
      va_start (arg, flags);

      /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
         creates crashing code when 'mode_t' is smaller than 'int'.  */
      mode = va_arg (arg, PROMOTED_MODE_T);

      va_end (arg);
    }

#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
  if (strcmp (filename, "/dev/null") == 0)
    filename = "NUL";
#endif

#if OPEN_TRAILING_SLASH_BUG
  /* If the filename ends in a slash and one of O_CREAT, O_WRONLY, O_RDWR
     is specified, then fail.
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
     says that
       "A pathname that contains at least one non-slash character and that
        ends with one or more trailing slashes shall be resolved as if a
        single dot character ( '.' ) were appended to the pathname."
     and
       "The special filename dot shall refer to the directory specified by
        its predecessor."
     If the named file already exists as a directory, then
       - if O_CREAT is specified, open() must fail because of the semantics
         of O_CREAT,
       - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
         <http://www.opengroup.org/susv3/functions/open.html> says that it
         fails with errno = EISDIR in this case.
     If the named file does not exist or does not name a directory, then
       - if O_CREAT is specified, open() must fail since open() cannot create
         directories,
       - if O_WRONLY or O_RDWR is specified, open() must fail because the
         file does not contain a '.' directory.  */
  if (flags & (O_CREAT | O_WRONLY | O_RDWR))
    {
      size_t len = strlen (filename);
      if (len > 0 && filename[len - 1] == '/')
        {
          errno = EISDIR;
          return -1;
        }
    }
#endif

  fd = orig_open (filename, flags, mode);

#if REPLACE_FCHDIR
  /* Implementing fchdir and fdopendir requires the ability to open a
     directory file descriptor.  If open doesn't support that (as on
     mingw), we use a dummy file that behaves the same as directories
     on Linux (ie. always reports EOF on attempts to read()), and
     override fstat() in fchdir.c to hide the fact that we have a
     dummy.  */
  if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES
      && (flags & O_ACCMODE) == O_RDONLY)
    {
      struct stat statbuf;
      if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
        {
          /* Maximum recursion depth of 1.  */
          fd = open ("/dev/null", flags, mode);
          if (0 <= fd)
            fd = _gl_register_fd (fd, filename);
        }
      else
        errno = EACCES;
    }
#endif

#if OPEN_TRAILING_SLASH_BUG
  /* If the filename ends in a slash and fd does not refer to a directory,
     then fail.
     Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
     says that
       "A pathname that contains at least one non-slash character and that
        ends with one or more trailing slashes shall be resolved as if a
        single dot character ( '.' ) were appended to the pathname."
     and
       "The special filename dot shall refer to the directory specified by
        its predecessor."
     If the named file without the slash is not a directory, open() must fail
     with ENOTDIR.  */
  if (fd >= 0)
    {
      /* We know len is positive, since open did not fail with ENOENT.  */
      size_t len = strlen (filename);
      if (filename[len - 1] == '/')
        {
          struct stat statbuf;

          if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
            {
              close (fd);
              errno = ENOTDIR;
              return -1;
            }
        }
    }
#endif

#if REPLACE_FCHDIR
  if (!REPLACE_OPEN_DIRECTORY && 0 <= fd)
    fd = _gl_register_fd (fd, filename);
#endif

  return fd;
}