changeset 12263:c689d1419da9

link: detect FreeBSD bug link("link-to-file/","a") mistakenly created "a" as a link to "file". * m4/link.m4 (gl_FUNC_LINK): Also detect FreeBSD bug with slash on symlink. * doc/posix-functions/link.texi (link): Document the bug. * tests/test-link.h (test_link): Enhance test. * tests/test-linkat.c (main): Update caller. Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Mon, 09 Nov 2009 14:23:11 -0700
parents 29121a37225b
children 2518d73aaac7
files ChangeLog doc/posix-functions/link.texi m4/link.m4 tests/test-link.h tests/test-linkat.c
diffstat 5 files changed, 51 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2009-11-10  Eric Blake  <ebb9@byu.net>
 
+	link: detect FreeBSD bug
+	* m4/link.m4 (gl_FUNC_LINK): Also detect FreeBSD bug with slash on
+	symlink.
+	* doc/posix-functions/link.texi (link): Document the bug.
+	* tests/test-link.h (test_link): Enhance test.
+	* tests/test-linkat.c (main): Update caller.
+
 	unlink, remove: detect FreeBSD bug
 	* m4/unlink.m4 (gl_FUNC_UNLINK): Also detect FreeBSD bug with
 	slash on symlink.
--- a/doc/posix-functions/link.texi
+++ b/doc/posix-functions/link.texi
@@ -14,7 +14,7 @@
 @item
 This function fails to reject trailing slashes on non-directories on
 some platforms:
-Solaris, Cygwin 1.5.x.
+FreeBSD 7.2, Solaris, Cygwin 1.5.x.
 @end itemize
 
 Portability problems not fixed by Gnulib:
--- a/m4/link.m4
+++ b/m4/link.m4
@@ -1,4 +1,4 @@
-# link.m4 serial 3
+# link.m4 serial 4
 dnl Copyright (C) 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -15,13 +15,21 @@
     AC_CACHE_CHECK([whether link handles trailing slash correctly],
       [gl_cv_func_link_works],
       [touch conftest.a
+       # Assume that if we have lstat, we can also check symlinks.
+       if test $ac_cv_func_lstat = yes; then
+         ln -s conftest.a conftest.lnk
+       fi
        AC_RUN_IFELSE(
          [AC_LANG_PROGRAM(
            [[#include <unistd.h>
-]], [[return !link ("conftest.a", "conftest.b/");]])],
+]], [[if (!link ("conftest.a", "conftest.b/")) return 1;
+#if HAVE_LSTAT
+      if (!link ("conftest.lnk/", "conftest.b")) return 2;
+#endif
+           ]])],
          [gl_cv_func_link_works=yes], [gl_cv_func_link_works=no],
          [gl_cv_func_link_works="guessing no"])
-       rm -f conftest.a conftest.b])
+       rm -f conftest.a conftest.b conftest.lnk])
     if test "$gl_cv_func_link_works" != yes; then
       REPLACE_LINK=1
       AC_LIBOBJ([link])
--- a/tests/test-link.h
+++ b/tests/test-link.h
@@ -18,8 +18,8 @@
    linkat(AT_FDCWD,a,AT_FDCWD,b,0).  FUNC is the function to test.
    Assumes that BASE and ASSERT are already defined, and that
    appropriate headers are already included.  If PRINT, warn before
-   skipping tests with status 77.  This test does not exercise link on
-   symlinks.  */
+   skipping tests with status 77.  This test does not try to create
+   hard links to symlinks, but does test other aspects of symlink.  */
 
 static int
 test_link (int (*func) (char const *, char const *), bool print)
@@ -145,14 +145,32 @@
         ASSERT (errno == EPERM || errno == EACCES || errno == EINVAL);
       }
   }
-
-  /* Clean up.  */
   ASSERT (unlink (BASE "a") == 0);
-  ASSERT (unlink (BASE "b") == 0);
   errno = 0;
   ASSERT (unlink (BASE "c") == -1);
   ASSERT (errno == ENOENT);
   ASSERT (rmdir (BASE "d") == 0);
 
+  /* Test invalid use of symlink.  */
+  if (symlink (BASE "a", BASE "link") != 0)
+    {
+      ASSERT (unlink (BASE "b") == 0);
+      if (print)
+        fputs ("skipping test: symlinks not supported on this file system\n",
+               stderr);
+      return 77;
+    }
+  errno = 0;
+  ASSERT (func (BASE "b", BASE "link/") == -1);
+  ASSERT (errno == ENOTDIR || errno == ENOENT || errno == EEXIST);
+  ASSERT (rename (BASE "b", BASE "a") == 0);
+  errno = 0;
+  ASSERT (func (BASE "link/", BASE "b") == -1);
+  ASSERT (errno == ENOTDIR || errno == EEXIST);
+
+  /* Clean up.  */
+  ASSERT (unlink (BASE "a") == 0);
+  ASSERT (unlink (BASE "link") == 0);
+
   return 0;
 }
--- a/tests/test-linkat.c
+++ b/tests/test-linkat.c
@@ -36,11 +36,11 @@
   do                                                                         \
     {                                                                        \
       if (!(expr))                                                           \
-	{                                                                    \
-	  fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
-	  fflush (stderr);                                                   \
-	  abort ();                                                          \
-	}                                                                    \
+        {                                                                    \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
+          fflush (stderr);                                                   \
+          abort ();                                                          \
+        }                                                                    \
     }                                                                        \
   while (0)
 
@@ -92,7 +92,7 @@
   ASSERT (system ("rm -rf " BASE "*") == 0);
 
   /* Test basic link functionality, without mentioning symlinks.  */
-  result = test_link (do_link, false);
+  result = test_link (do_link, true);
   dfd1 = open (".", O_RDONLY);
   ASSERT (0 <= dfd1);
   ASSERT (test_link (do_link, false) == result);
@@ -166,8 +166,9 @@
       ASSERT (rmdir (BASE "sub1") == 0);
       ASSERT (rmdir (BASE "sub2") == 0);
       free (cwd);
-      fputs ("skipping test: symlinks not supported on this file system\n",
-             stderr);
+      if (!result)
+        fputs ("skipping test: symlinks not supported on this file system\n",
+               stderr);
       return result;
     }
   dfd = open (".", O_RDONLY);