changeset 12047:21ec8b9a3f27

openat: fix openat bugs on Solaris 9 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>
author Eric Blake <ebb9@byu.net>
date Sat, 19 Sep 2009 11:16:58 -0600
parents fb58acddad72
children 0f01e7ffb856
files 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
diffstat 8 files changed, 125 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- 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.
--- a/doc/posix-functions/openat.texi
+++ b/doc/posix-functions/openat.texi
@@ -13,6 +13,11 @@
 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:
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -62,9 +62,11 @@
 #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
--- a/lib/openat.c
+++ b/lib/openat.c
@@ -22,12 +22,107 @@
 
 #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 @@
 
   return needs_fchdir;
 }
+
+#endif /* !HAVE_OPENAT */
--- a/m4/fcntl_h.m4
+++ b/m4/fcntl_h.m4
@@ -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_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])
 ])
--- a/m4/openat.m4
+++ b/m4/openat.m4
@@ -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 @@
   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])
--- a/modules/fcntl-h
+++ b/modules/fcntl-h
@@ -28,6 +28,7 @@
 	      -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; \
--- a/modules/openat
+++ b/modules/openat
@@ -27,6 +27,7 @@
 intprops
 lchown
 lstat
+open
 openat-die
 rmdir
 same-inode