1# serial 14
2# See if we need to provide linkat replacement.
3
4dnl Copyright (C) 2009-2021 Free Software Foundation, Inc.
5dnl This file is free software; the Free Software Foundation
6dnl gives unlimited permission to copy and/or distribute it,
7dnl with or without modifications, as long as this notice is preserved.
8
9# Written by Eric Blake.
10
11AC_DEFUN([gl_FUNC_LINKAT],
12[
13  AC_REQUIRE([gl_FUNC_OPENAT])
14  AC_REQUIRE([gl_FUNC_LINK_FOLLOWS_SYMLINK])
15  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
16  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
17  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
18  AC_CHECK_FUNCS_ONCE([linkat symlink])
19  if test $ac_cv_func_linkat = no; then
20    HAVE_LINKAT=0
21  else
22    dnl OS X Yosemite has linkat() but it's not sufficient
23    dnl to our needs since it doesn't support creating
24    dnl hardlinks to symlinks.  Therefore check for that
25    dnl capability before considering using the system version.
26    AC_CACHE_CHECK([whether linkat() can link symlinks],
27      [gl_cv_func_linkat_nofollow],
28      [rm -rf conftest.l1 conftest.l2
29       ln -s target conftest.l1
30       AC_RUN_IFELSE([AC_LANG_PROGRAM(
31                        [[#include <fcntl.h>
32                          #include <unistd.h>
33                        ]],
34                        [[return linkat (AT_FDCWD, "conftest.l1", AT_FDCWD,
35                                         "conftest.l2", 0);
36                        ]])],
37         [gl_cv_func_linkat_nofollow=yes],
38         [gl_cv_func_linkat_nofollow=no],
39         [case "$host_os" in
40           darwin*) gl_cv_func_linkat_nofollow="guessing no" ;;
41           *)       gl_cv_func_linkat_nofollow="guessing yes" ;;
42          esac])
43
44       rm -rf conftest.l1 conftest.l2])
45
46    case $gl_cv_func_linkat_nofollow in
47      *no) LINKAT_SYMLINK_NOTSUP=1 ;;
48      *yes) LINKAT_SYMLINK_NOTSUP=0 ;;
49    esac
50
51    AC_CACHE_CHECK([whether linkat handles trailing slash correctly],
52      [gl_cv_func_linkat_slash],
53      [rm -rf conftest.a conftest.b conftest.c conftest.d conftest.e conftest.s
54       AC_RUN_IFELSE(
55         [AC_LANG_PROGRAM(
56            [[#include <unistd.h>
57              #include <fcntl.h>
58              #include <errno.h>
59              #include <stdio.h>
60            ]GL_MDA_DEFINES],
61            [[int result;
62              int fd;
63              /* Create a regular file.  */
64              fd = open ("conftest.a", O_CREAT | O_EXCL | O_WRONLY, 0600);
65              if (fd < 0)
66                return 1;
67              if (write (fd, "hello", 5) < 5)
68                return 2;
69              if (close (fd) < 0)
70                return 3;
71              /* Test whether hard links are supported on the current
72                 device.  */
73              if (linkat (AT_FDCWD, "conftest.a", AT_FDCWD, "conftest.b",
74                          AT_SYMLINK_FOLLOW) < 0)
75                return 0;
76              result = 0;
77              /* Test whether a trailing "/" is treated like "/.".  */
78              if (linkat (AT_FDCWD, "conftest.a/", AT_FDCWD, "conftest.c",
79                          AT_SYMLINK_FOLLOW) == 0)
80                result |= 4;
81              if (linkat (AT_FDCWD, "conftest.a", AT_FDCWD, "conftest.d/",
82                          AT_SYMLINK_FOLLOW) == 0)
83                result |= 8;
84
85              /* On Mac OS X 10.13 a trailing "/" will cause the second path to be
86                 dereferenced, and thus will succeed on a dangling symlink.  */
87              if (symlink ("conftest.e", "conftest.s") == 0)
88                {
89                  if (linkat (AT_FDCWD, "conftest.a", AT_FDCWD, "conftest.s/",
90                      AT_SYMLINK_FOLLOW) == 0)
91                    result |= 16;
92                }
93
94              return result;
95            ]])],
96         [gl_cv_func_linkat_slash=yes],
97         [gl_cv_func_linkat_slash=no],
98         [
99          case "$host_os" in
100                             # Guess yes on Linux systems.
101            linux-* | linux) gl_cv_func_linkat_slash="guessing yes";;
102                             # Guess yes on glibc systems.
103            *-gnu* | gnu*)   gl_cv_func_linkat_slash="guessing yes";;
104                             # If we don't know, obey --enable-cross-guesses.
105            *)               gl_cv_func_linkat_slash="$gl_cross_guess_normal";;
106          esac
107         ])
108       rm -rf conftest.a conftest.b conftest.c conftest.d conftest.e conftest.s])
109    case "$gl_cv_func_linkat_slash" in
110      *yes) gl_linkat_slash_bug=0 ;;
111      *)    gl_linkat_slash_bug=1 ;;
112    esac
113
114    case "$gl_cv_func_linkat_nofollow" in
115      *yes) linkat_nofollow=yes ;;
116      *) linkat_nofollow=no ;;
117    esac
118
119    if test "$linkat_nofollow" != yes \
120       || test $gl_linkat_slash_bug = 1; then
121      REPLACE_LINKAT=1
122      AC_DEFINE_UNQUOTED([LINKAT_TRAILING_SLASH_BUG], [$gl_linkat_slash_bug],
123        [Define to 1 if linkat fails to recognize a trailing slash.])
124      AC_DEFINE_UNQUOTED([LINKAT_SYMLINK_NOTSUP], [$LINKAT_SYMLINK_NOTSUP],
125        [Define to 1 if linkat can create hardlinks to symlinks])
126    fi
127  fi
128])
129