From df1c2344f29dbca57d9e560aaa89aa2c20cdda7f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 22 Jun 2011 12:15:02 -0600 Subject: [PATCH] link: work around IRIX bug On IRIX 6.5, link(file, "dangling") creates the target of dangling as a link to file, rather than failing with EEXIST. * m4/link.m4 (gl_FUNC_LINK): Expose the bug. * lib/link.c (rpl_link): Work around it. * tests/test-link.h (test_link): Enhance test. * doc/posix-functions/link.texi (link): Document the bug. Signed-off-by: Eric Blake --- ChangeLog | 6 ++++++ doc/posix-functions/link.texi | 4 ++++ lib/link.c | 17 +++++++++++++---- m4/link.m4 | 8 ++++++-- tests/test-link.h | 3 +++ 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8ca76fab7a..440a244abd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2011-06-22 Eric Blake + link: work around IRIX bug + * m4/link.m4 (gl_FUNC_LINK): Expose the bug. + * lib/link.c (rpl_link): Work around it. + * tests/test-link.h (test_link): Enhance test. + * doc/posix-functions/link.texi (link): Document the bug. + getopt: silence clang warning * lib/getopt.c (_getopt_internal_r): Avoid unlikely NULL dereference. diff --git a/doc/posix-functions/link.texi b/doc/posix-functions/link.texi index 172037c899..d4f2a7e0b4 100644 --- a/doc/posix-functions/link.texi +++ b/doc/posix-functions/link.texi @@ -15,6 +15,10 @@ mingw. This function fails to reject trailing slashes on non-directories on some platforms: FreeBSD 7.2, Solaris 11 2010-11, Cygwin 1.5.x. +@item +When the second argument is a dangling symlink, some platforms follow +that link and create the destination rather than failing: +IRIX 6.5. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/link.c b/lib/link.c index 1b0fe0a0e9..b58eb25bc3 100644 --- a/lib/link.c +++ b/lib/link.c @@ -155,9 +155,20 @@ link (const char *file1, const char *file2) int rpl_link (char const *file1, char const *file2) { + size_t len1; + size_t len2; + struct stat st; + + /* Don't allow IRIX to dereference dangling file2 symlink. */ + if (!lstat (file2, &st)) + { + errno = EEXIST; + return -1; + } + /* Reject trailing slashes on non-directories. */ - size_t len1 = strlen (file1); - size_t len2 = strlen (file2); + len1 = strlen (file1); + len2 = strlen (file2); if ((len1 && file1[len1 - 1] == '/') || (len2 && file2[len2 - 1] == '/')) { @@ -165,7 +176,6 @@ rpl_link (char const *file1, char const *file2) If stat() fails, then link() should fail for the same reason (although on Solaris 9, link("file/","oops") mistakenly succeeds); if stat() succeeds, require a directory. */ - struct stat st; if (stat (file1, &st)) return -1; if (!S_ISDIR (st.st_mode)) @@ -178,7 +188,6 @@ rpl_link (char const *file1, char const *file2) { /* Fix Cygwin 1.5.x bug where link("a","b/.") creates file "b". */ char *dir = strdup (file2); - struct stat st; char *p; if (!dir) return -1; diff --git a/m4/link.m4 b/m4/link.m4 index 2f1a439ede..ed8427a9b2 100644 --- a/m4/link.m4 +++ b/m4/link.m4 @@ -1,4 +1,4 @@ -# link.m4 serial 6 +# link.m4 serial 7 dnl Copyright (C) 2009-2011 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -11,7 +11,7 @@ AC_DEFUN([gl_FUNC_LINK], if test $ac_cv_func_link = no; then HAVE_LINK=0 else - AC_CACHE_CHECK([whether link handles trailing slash correctly], + AC_CACHE_CHECK([whether link obeys POSIX], [gl_cv_func_link_works], [touch conftest.a # Assume that if we have lstat, we can also check symlinks. @@ -28,6 +28,10 @@ AC_DEFUN([gl_FUNC_LINK], #if HAVE_LSTAT if (!link ("conftest.lnk/", "conftest.b")) result |= 2; + if (rename ("conftest.a", "conftest.b")) + result |= 4; + if (!link ("conftest.b", "conftest.lnk")) + result |= 8; #endif return result; ]])], diff --git a/tests/test-link.h b/tests/test-link.h index 5e90951b94..5c50da0a9d 100644 --- a/tests/test-link.h +++ b/tests/test-link.h @@ -166,6 +166,9 @@ test_link (int (*func) (char const *, char const *), bool print) ASSERT (func (BASE "b", BASE "link/") == -1); ASSERT (errno == ENOTDIR || errno == ENOENT || errno == EEXIST || errno == EINVAL); + errno = 0; + ASSERT (func (BASE "b", BASE "link") == -1); + ASSERT (errno == EEXIST); ASSERT (rename (BASE "b", BASE "a") == 0); errno = 0; ASSERT (func (BASE "link/", BASE "b") == -1); -- 2.30.2