openat: fix openat bugs on Solaris 9
authorEric Blake <ebb9@byu.net>
Sat, 19 Sep 2009 17:16:58 +0000 (11:16 -0600)
committerEric Blake <ebb9@byu.net>
Sat, 19 Sep 2009 19:53:27 +0000 (13:53 -0600)
openat(fd,"file/",O_RDONLY) mistakenly succeeded.

* lib/openat.c (rpl_openat): Work around Solaris 9 bug.
* m4/openat.m4 (gl_FUNC_OPENAT): Also replace openat on Solaris.
* modules/openat (Depends-on): Add open.
* m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Provide new default.
* modules/fcntl-h (Makefile.am): Substitute it.
* lib/fcntl.in.h (openat): Declare replacement.
* doc/posix-functions/openat.texi (openat): Document this.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
doc/posix-functions/openat.texi
lib/fcntl.in.h
lib/openat.c
m4/fcntl_h.m4
m4/openat.m4
modules/fcntl-h
modules/openat

index 1b9f09a..94b8465 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2009-09-19  Eric Blake  <ebb9@byu.net>
 
+       openat: fix openat bugs on Solaris 9
+       * lib/openat.c (rpl_openat): Work around Solaris 9 bug.
+       * m4/openat.m4 (gl_FUNC_OPENAT): Also replace openat on Solaris.
+       * modules/openat (Depends-on): Add open.
+       * m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Provide new default.
+       * modules/fcntl-h (Makefile.am): Substitute it.
+       * lib/fcntl.in.h (openat): Declare replacement.
+       * doc/posix-functions/openat.texi (openat): Document this.
+
        openat: move fstatat and unlinkat into correct files
        * m4/openat.m4 (gl_FUNC_OPENAT): Adjust which files will be
        compiled.
index 5a99e0c..2bfe611 100644 (file)
@@ -13,6 +13,11 @@ This function is missing on some platforms:
 glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX
 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin 1.5.x, mingw, Interix 3.5, BeOS.
 But the replacement function is not safe to be used in libraries and is not multithread-safe.
+@item
+This function does not fail when the file name argument ends in a slash
+and (without the slash) names a nonexistent file or a file that is not a
+directory, on some platforms:
+Solaris 9.
 @end itemize
 
 Portability problems not fixed by Gnulib:
index cadb6a1..0ae8213 100644 (file)
@@ -62,9 +62,11 @@ extern int open (const char *filename, int flags, ...);
 #endif
 
 #if @GNULIB_OPENAT@
-# if !@HAVE_OPENAT@
+# if @REPLACE_OPENAT@
 #  undef openat
 #  define openat rpl_openat
+# endif
+# if !@HAVE_OPENAT@ || @REPLACE_OPENAT@
 int openat (int fd, char const *file, int flags, /* mode_t mode */ ...);
 # endif
 #elif defined GNULIB_POSIXCHECK
index 2a194e8..7e46a26 100644 (file)
 
 #include <stdarg.h>
 #include <stddef.h>
+#include <string.h>
 #include <sys/stat.h>
 
 #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
 #include "openat-priv.h"
 #include "save-cwd.h"
 
+#if HAVE_OPENAT
+
+# undef openat
+
+/* Like openat, but work around Solaris 9 bugs with trailing slash.  */
+int
+rpl_openat (int dfd, char const *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 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 = openat (dfd, filename, flags, mode);
+
+#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)
+    {
+      size_t len = strlen (filename);
+      if (len > 0 && filename[len - 1] == '/')
+       {
+         struct stat statbuf;
+
+         if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
+           {
+             close (fd);
+             errno = ENOTDIR;
+             return -1;
+           }
+       }
+    }
+#endif
+
+  return fd;
+}
+
+#else /* !HAVE_OPENAT */
+
 /* Replacement for Solaris' openat function.
    <http://www.google.com/search?q=openat+site:docs.sun.com>
    First, try to simulate it via open ("/proc/self/fd/FD/FILE").
@@ -156,3 +251,5 @@ openat_needs_fchdir (void)
 
   return needs_fchdir;
 }
+
+#endif /* !HAVE_OPENAT */
index 7100d19..5eed088 100644 (file)
@@ -1,4 +1,4 @@
-# serial 3
+# serial 4
 # Configure fcntl.h.
 dnl Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
@@ -91,9 +91,10 @@ AC_DEFUN([gl_FCNTL_MODULE_INDICATOR],
 
 AC_DEFUN([gl_FCNTL_H_DEFAULTS],
 [
-  GNULIB_OPEN=0;   AC_SUBST([GNULIB_OPEN])
-  GNULIB_OPENAT=0; AC_SUBST([GNULIB_OPENAT])
+  GNULIB_OPEN=0;    AC_SUBST([GNULIB_OPEN])
+  GNULIB_OPENAT=0;  AC_SUBST([GNULIB_OPENAT])
   dnl Assume proper GNU behavior unless another module says otherwise.
-  HAVE_OPENAT=1;  AC_SUBST([HAVE_OPENAT])
-  REPLACE_OPEN=0; AC_SUBST([REPLACE_OPEN])
+  HAVE_OPENAT=1;    AC_SUBST([HAVE_OPENAT])
+  REPLACE_OPEN=0;   AC_SUBST([REPLACE_OPEN])
+  REPLACE_OPENAT=0; AC_SUBST([REPLACE_OPENAT])
 ])
index b824393..42df3ee 100644 (file)
@@ -1,4 +1,4 @@
-# serial 23
+# serial 24
 # See if we need to use our replacement for Solaris' openat et al functions.
 
 dnl Copyright (C) 2004-2009 Free Software Foundation, Inc.
@@ -32,6 +32,8 @@ AC_DEFUN([gl_FUNC_OPENAT],
   yes+*)
     # Solaris 9 has *at functions, but uniformly mishandles trailing
     # slash in all of them.
+    AC_LIBOBJ([openat])
+    REPLACE_OPENAT=1
     AC_LIBOBJ([fstatat])
     REPLACE_FSTATAT=1
     AC_LIBOBJ([unlinkat])
index 2b811d1..ea76181 100644 (file)
@@ -28,6 +28,7 @@ fcntl.h: fcntl.in.h
              -e 's|@''GNULIB_OPEN''@|$(GNULIB_OPEN)|g' \
              -e 's|@''GNULIB_OPENAT''@|$(GNULIB_OPENAT)|g' \
              -e 's|@''REPLACE_OPEN''@|$(REPLACE_OPEN)|g' \
+             -e 's|@''REPLACE_OPENAT''@|$(REPLACE_OPENAT)|g' \
              -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \
              -e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
              < $(srcdir)/fcntl.in.h; \
index 8cb1345..150853f 100644 (file)
@@ -27,6 +27,7 @@ inline
 intprops
 lchown
 lstat
+open
 openat-die
 rmdir
 same-inode