From 4c45e93c58de6532275c22a9153ecdfe516928ff Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Sat, 19 Sep 2009 11:16:58 -0600 Subject: [PATCH] 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 --- ChangeLog | 9 ++++ doc/posix-functions/openat.texi | 5 +++ lib/fcntl.in.h | 4 +- lib/openat.c | 97 +++++++++++++++++++++++++++++++++++++++++ m4/fcntl_h.m4 | 11 ++--- m4/openat.m4 | 4 +- modules/fcntl-h | 1 + modules/openat | 1 + 8 files changed, 125 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1b9f09a9c..94b8465e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2009-09-19 Eric Blake + 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. diff --git a/doc/posix-functions/openat.texi b/doc/posix-functions/openat.texi index 5a99e0cdb..2bfe61163 100644 --- a/doc/posix-functions/openat.texi +++ b/doc/posix-functions/openat.texi @@ -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: diff --git a/lib/fcntl.in.h b/lib/fcntl.in.h index cadb6a157..0ae8213e4 100644 --- a/lib/fcntl.in.h +++ b/lib/fcntl.in.h @@ -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 diff --git a/lib/openat.c b/lib/openat.c index 2a194e885..7e46a2672 100644 --- a/lib/openat.c +++ b/lib/openat.c @@ -22,12 +22,107 @@ #include #include +#include #include #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 + 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 + 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 + 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. 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 */ diff --git a/m4/fcntl_h.m4 b/m4/fcntl_h.m4 index 7100d1980..5eed0888b 100644 --- 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_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]) ]) diff --git a/m4/openat.m4 b/m4/openat.m4 index b824393e5..42df3ee72 100644 --- 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 @@ 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]) diff --git a/modules/fcntl-h b/modules/fcntl-h index 2b811d1ba..ea76181b4 100644 --- a/modules/fcntl-h +++ b/modules/fcntl-h @@ -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; \ diff --git a/modules/openat b/modules/openat index 8cb13455a..150853f4c 100644 --- a/modules/openat +++ b/modules/openat @@ -27,6 +27,7 @@ inline intprops lchown lstat +open openat-die rmdir same-inode -- 2.11.0