From 37da693c0a2d4d84369d1910d258370ce65552ce Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 28 Dec 2010 12:18:54 +0100 Subject: [PATCH] linkat: Make implementation robust against system behaviour variations. * m4/link-follow.m4 (gl_FUNC_LINK_FOLLOWS_SYMLINK): Define LINK_FOLLOWS_SYMLINKS to -1 if it needs a runtime test in the Solaris way, and to -2 if it needs a generic runtime test. * lib/linkat.c (solaris_optimized_link_immediate, solaris_optimized_link_follow): New functions. * tests/test-linkat.c (EXPECT_LINK_HARDLINKS_SYMLINKS): New macro. (check_same_link): Use it. --- ChangeLog | 12 +++++++++++ lib/linkat.c | 30 +++++++++++++++++++++++++++ m4/link-follow.m4 | 60 ++++++++++++++++++++++++++++++++++------------------- tests/test-linkat.c | 13 +++++++++++- 4 files changed, 93 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index fd3d4a216..0d175a2f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2010-12-28 Bruno Haible + Paul Eggert + + linkat: Make implementation robust against system behaviour variations. + * m4/link-follow.m4 (gl_FUNC_LINK_FOLLOWS_SYMLINK): Define + LINK_FOLLOWS_SYMLINKS to -1 if it needs a runtime test in the Solaris + way, and to -2 if it needs a generic runtime test. + * lib/linkat.c (solaris_optimized_link_immediate, + solaris_optimized_link_follow): New functions. + * tests/test-linkat.c (EXPECT_LINK_HARDLINKS_SYMLINKS): New macro. + (check_same_link): Use it. + 2010-12-26 Ben Pfaff New module 'unigbrk/base'. diff --git a/lib/linkat.c b/lib/linkat.c index 73b1e3ed6..e611ad499 100644 --- a/lib/linkat.c +++ b/lib/linkat.c @@ -166,6 +166,36 @@ link_follow (char const *file1, char const *file2) } # endif /* 0 < LINK_FOLLOWS_SYMLINKS */ +/* On Solaris, link() doesn't follow symlinks by default, but does so as soon + as a library or executable takes part in the program that has been compiled + with "c99" or "cc -xc99=all" or "cc ... /usr/lib/values-xpg4.o ...". */ +# if LINK_FOLLOWS_SYMLINKS == -1 + +/* Reduce the penalty of link_immediate and link_follow by incorporating the + knowledge that link()'s behaviour depends on the __xpg4 variable. */ +extern int __xpg4; + +static int +solaris_optimized_link_immediate (char const *file1, char const *file2) +{ + if (__xpg4 == 0) + return link (file1, file2); + return link_immediate (file1, file2); +} + +static int +solaris_optimized_link_follow (char const *file1, char const *file2) +{ + if (__xpg4 != 0) + return link (file1, file2); + return link_follow (file1, file2); +} + +# define link_immediate solaris_optimized_link_immediate +# define link_follow solaris_optimized_link_follow + +# endif + /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2, in the directory open on descriptor FD2. If FILE1 is a symlink, FLAG controls whether to dereference FILE1 first. If possible, do it without diff --git a/m4/link-follow.m4 b/m4/link-follow.m4 index 256b2a99d..95264db26 100644 --- a/m4/link-follow.m4 +++ b/m4/link-follow.m4 @@ -1,4 +1,4 @@ -# serial 15 +# serial 16 dnl Run a program to determine whether link(2) follows symlinks. dnl Set LINK_FOLLOWS_SYMLINKS accordingly. @@ -12,7 +12,8 @@ dnl LINK_FOLLOWS_SYMLINKS is 0, link matches linkat(,0), and dnl linkat(,AT_SYMLINK_FOLLOW) requires a readlink. If it is 1, dnl link matches linkat(,AT_SYMLINK_FOLLOW), and there is no way dnl to do linkat(,0) on symlinks (on all other file types, -dnl link() is sufficient). If it is -1, use a runtime test. +dnl link() is sufficient). If it is -1, use a Solaris specific +dnl runtime test. If it is -2, use a generic runtime test. AC_DEFUN([gl_FUNC_LINK_FOLLOWS_SYMLINK], [dnl AC_CHECK_FUNCS_ONCE([readlink]) @@ -22,12 +23,27 @@ AC_DEFUN([gl_FUNC_LINK_FOLLOWS_SYMLINK], dnl linkat variants. So, we set LINK_FOLLOWS_SYMLINKS to 0. gl_link_follows_symlinks=0 # assume GNU behavior if test $ac_cv_func_readlink = yes; then - AC_CACHE_CHECK([whether link(2) dereferences a symlink], - gl_cv_func_link_follows_symlink, - [ - # Create a regular file. - echo > conftest.file - AC_RUN_IFELSE([AC_LANG_SOURCE([[ + dnl Solaris has an __xpg4 variable in libc, and it determines the + dnl behaviour of link(): It dereferences a symlink if and only if + dnl __xpg4 != 0. + AC_CACHE_CHECK([for __xpg4], [gl_cv_have___xpg4], + [AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[extern int __xpg4;]], + [[return __xpg4;]])], + [gl_cv_have___xpg4=yes], + [gl_cv_have___xpg4=no]) + ]) + if test $gl_cv_have___xpg4 = yes; then + gl_link_follows_symlinks=-1 + else + AC_CACHE_CHECK([whether link(2) dereferences a symlink], + [gl_cv_func_link_follows_symlink], + [ + # Create a regular file. + echo > conftest.file + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ # include # include # include @@ -62,20 +78,22 @@ AC_DEFUN([gl_FUNC_LINK_FOLLOWS_SYMLINK], the link call followed the symlink. */ return SAME_INODE (sb_hard, sb_file) ? 1 : 0; } - ]])], - [gl_cv_func_link_follows_symlink=no], dnl GNU behavior - [gl_cv_func_link_follows_symlink=yes], dnl Followed link/compile failed - [gl_cv_func_link_follows_symlink=unknown] dnl We're cross compiling. - ) - rm -f conftest.file conftest.sym conftest.hard - ]) - case $gl_cv_func_link_follows_symlink in - yes) gl_link_follows_symlinks=1 ;; - no) ;; # already defaulted to 0 - *) gl_link_follows_symlinks=-1 ;; - esac + ]])], + [gl_cv_func_link_follows_symlink=no], dnl GNU behavior + [gl_cv_func_link_follows_symlink=yes], dnl Followed link/compile failed + [gl_cv_func_link_follows_symlink=unknown] dnl We're cross compiling. + ) + rm -f conftest.file conftest.sym conftest.hard + ]) + case $gl_cv_func_link_follows_symlink in + yes) gl_link_follows_symlinks=1 ;; + no) ;; # already defaulted to 0 + *) gl_link_follows_symlinks=-2 ;; + esac + fi fi AC_DEFINE_UNQUOTED([LINK_FOLLOWS_SYMLINKS], [$gl_link_follows_symlinks], [Define to 1 if `link(2)' dereferences symbolic links, 0 if it - creates hard links to symlinks, and -1 if unknown.]) + creates hard links to symlinks, -1 if it depends on the variable __xpg4, + and -2 if unknown.]) ]) diff --git a/tests/test-linkat.c b/tests/test-linkat.c index 9d7a0a7fd..1f7354470 100644 --- a/tests/test-linkat.c +++ b/tests/test-linkat.c @@ -53,6 +53,17 @@ do_link (char const *name1, char const *name2) return linkat (dfd1, name1, dfd2, name2, flag); } +/* Can we expect that link() and linkat(), when called on a symlink, + increment the link count of that symlink? */ +#if LINK_FOLLOWS_SYMLINKS == 0 +# define EXPECT_LINK_HARDLINKS_SYMLINKS 1 +#elif LINK_FOLLOWS_SYMLINKS == -1 +extern int __xpg4; +# define EXPECT_LINK_HARDLINKS_SYMLINKS (__xpg4 == 0) +#else +# define EXPECT_LINK_HARDLINKS_SYMLINKS 0 +#endif + /* Wrapper to see if two symlinks act the same. */ static void check_same_link (char const *name1, char const *name2) @@ -68,7 +79,7 @@ check_same_link (char const *name1, char const *name2) ASSERT (contents1); ASSERT (contents2); ASSERT (strcmp (contents1, contents2) == 0); - if (!LINK_FOLLOWS_SYMLINKS) + if (EXPECT_LINK_HARDLINKS_SYMLINKS) ASSERT (SAME_INODE (st1, st2)); free (contents1); free (contents2); -- 2.11.0