changeset 12044:1b85d0321310

openat: fix fstatat bugs on Solaris 9 fstatat(fd,"file/",buf,flag) mistakenly succeeded. * lib/fstatat.c (rpl_fstatat): Copy recent fixes from lstat and stat. * doc/posix-functions/fstatat.texi (fstatat): Document this. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Fri, 18 Sep 2009 19:38:46 -0600
parents fc84db4ef49d
children 2bd4d1ff29e9
files ChangeLog doc/posix-functions/fstatat.texi lib/fstatat.c
diffstat 3 files changed, 39 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2009-09-19  Eric Blake  <ebb9@byu.net>
 
+	openat: fix fstatat bugs on Solaris 9
+	* lib/fstatat.c (rpl_fstatat): Copy recent fixes from lstat and
+	stat.
+	* doc/posix-functions/fstatat.texi (fstatat): Document this.
+
 	test-unlinkat: enhance test, to expose Solaris 9 bug
 	* tests/test-unlink.c (main): Factor guts...
 	* tests/test-unlink.h (test_rmdir_func): ...into new file.
--- a/doc/posix-functions/fstatat.texi
+++ b/doc/posix-functions/fstatat.texi
@@ -13,8 +13,22 @@
 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
+On some platforms, @code{fstatat(fd,"file/",buf,flag)} succeeds instead of
+failing with @code{ENOTDIR}.
+Solaris 9.
+@item
+For symlinks, when the argument ends in a slash, some platforms don't
+dereference the argument:
+Solaris 9.
 @end itemize
 
 Portability problems not fixed by Gnulib:
 @itemize
+@item
+On platforms where @code{off_t} is a 32-bit type, @code{fstatat} may
+not correctly report the size of files or block devices larger than 2
+GB.  The fix is to use the @code{AC_SYS_LARGEFILE} macro.
+@item
+On Windows platforms (excluding Cygwin), @code{st_ino} is always 0.
 @end itemize
--- a/lib/fstatat.c
+++ b/lib/fstatat.c
@@ -28,31 +28,37 @@
 #undef fstatat
 
 /* fstatat should always follow symbolic links that end in /, but on
-   Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.  This is
-   the same problem that lstat.c addresses, so solve it in a similar
-   way.  */
+   Solaris 9 it doesn't if AT_SYMLINK_NOFOLLOW is specified.
+   Likewise, trailing slash on a non-directory should be an error.
+   These are the same problems that lstat.c and stat.c address, so
+   solve it in a similar way.  */
 
 int
 rpl_fstatat (int fd, char const *file, struct stat *st, int flag)
 {
   int result = fstatat (fd, file, st, flag);
+  size_t len;
 
-  if (result == 0 && (flag & AT_SYMLINK_NOFOLLOW) && S_ISLNK (st->st_mode)
-      && file[strlen (file) - 1] == '/')
+  if (result != 0)
+    return result;
+  len = strlen (file);
+  if (flag & AT_SYMLINK_NOFOLLOW)
     {
-      /* FILE refers to a symbolic link and the name ends with a slash.
-	 Get info about the link's referent.  */
-      result = fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
-      if (result == 0 && ! S_ISDIR (st->st_mode))
+      /* Fix lstat behavior.  */
+      if (file[len - 1] != '/' || S_ISDIR (st->st_mode))
+	return 0;
+      if (!S_ISLNK (st->st_mode))
 	{
-	  /* fstatat succeeded and FILE references a non-directory.
-	     But it was specified via a name including a trailing
-	     slash.  Fail with errno set to ENOTDIR to indicate the
-	     contradiction.  */
 	  errno = ENOTDIR;
 	  return -1;
 	}
+      result = fstatat (fd, file, st, flag & ~AT_SYMLINK_NOFOLLOW);
     }
-
+  /* Fix stat behavior.  */
+  if (result == 0 && !S_ISDIR (st->st_mode) && file[len - 1] == '/')
+    {
+      errno = ENOTDIR;
+      return -1;
+    }
   return result;
 }