openat: fix fstatat bugs on Solaris 9
authorEric Blake <ebb9@byu.net>
Sat, 19 Sep 2009 01:38:46 +0000 (19:38 -0600)
committerEric Blake <ebb9@byu.net>
Sat, 19 Sep 2009 19:41:14 +0000 (13:41 -0600)
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>
ChangeLog
doc/posix-functions/fstatat.texi
lib/fstatat.c

index 3c54062..e79f9dd 100644 (file)
--- 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.
index 4b9fcbc..d1c4c83 100644 (file)
@@ -13,8 +13,22 @@ 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
+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
index 9b0c1af..59d7422 100644 (file)
 #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;
 }