1# serial 15
2# Determine whether getcwd aborts when the length of the working directory
3# name is unusually large.  Any length between 4k and 16k trigger the bug
4# when using glibc-2.4.90-9 or older.
5
6# Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc.
7# This file is free software; the Free Software Foundation
8# gives unlimited permission to copy and/or distribute it,
9# with or without modifications, as long as this notice is preserved.
10
11# From Jim Meyering
12
13# gl_FUNC_GETCWD_ABORT_BUG([ACTION-IF-BUGGY[, ACTION-IF-WORKS]])
14AC_DEFUN([gl_FUNC_GETCWD_ABORT_BUG],
15[
16  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
17  AC_CHECK_DECLS_ONCE([getcwd])
18  AC_CHECK_HEADERS_ONCE([unistd.h])
19  AC_REQUIRE([gl_PATHMAX_SNIPPET_PREREQ])
20
21  gl_CHECK_FUNC_GETPAGESIZE
22  if test $gl_cv_func_getpagesize = yes; then
23    AC_DEFINE_UNQUOTED([HAVE_GETPAGESIZE], [1],
24      [Define to 1 if the system has the 'getpagesize' function.])
25  fi
26
27  AC_CACHE_CHECK([whether getcwd succeeds when 4k < cwd_length < 16k],
28    [gl_cv_func_getcwd_succeeds_beyond_4k],
29    [# Remove any remnants of a previous test.
30     rm -rf confdir-14B---
31     # Arrange for deletion of the temporary directory this test creates.
32     ac_clean_files="$ac_clean_files confdir-14B---"
33     dnl Please keep this in sync with tests/test-getcwd.c.
34     AC_RUN_IFELSE(
35       [AC_LANG_SOURCE(
36          [[
37#include <errno.h>
38#include <stdlib.h>
39#if HAVE_UNISTD_H
40# include <unistd.h>
41#else /* on Windows with MSVC */
42# include <direct.h>
43#endif
44#include <string.h>
45#include <sys/stat.h>
46
47]gl_PATHMAX_SNIPPET[
48]GL_MDA_DEFINES[
49
50#ifndef S_IRWXU
51# define S_IRWXU 0700
52#endif
53
54/* FIXME: skip the run-test altogether on systems without getpagesize.  */
55#if ! HAVE_GETPAGESIZE
56# define getpagesize() 0
57#endif
58
59/* This size is chosen to be larger than PATH_MAX (4k), yet smaller than
60   the 16kB pagesize on ia64 linux.  Those conditions make the code below
61   trigger a bug in glibc's getcwd implementation before 2.4.90-10.  */
62#define TARGET_LEN (5 * 1024)
63
64int
65main ()
66{
67  char *cwd;
68  size_t initial_cwd_len;
69  int fail = 0;
70
71  /* The bug is triggered when PATH_MAX < getpagesize (), so skip
72     this relatively expensive and invasive test if that's not true.  */
73#ifdef PATH_MAX
74  int bug_possible = PATH_MAX < getpagesize ();
75#else
76  int bug_possible = 0;
77#endif
78  if (! bug_possible)
79    return 0;
80
81  cwd = getcwd (NULL, 0);
82  if (cwd == NULL)
83    return 2;
84
85  initial_cwd_len = strlen (cwd);
86  free (cwd);
87
88  if (1)
89    {
90      static char const dir_name[] = "confdir-14B---";
91      size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
92                              / sizeof dir_name);
93      size_t d;
94      for (d = 0; d < desired_depth; d++)
95        {
96          if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
97            {
98              if (! (errno == ERANGE || errno == ENAMETOOLONG
99                     || errno == ENOENT))
100                fail = 3; /* Unable to construct deep hierarchy.  */
101              break;
102            }
103        }
104
105      /* If libc has the bug in question, this invocation of getcwd
106         results in a failed assertion.  */
107      cwd = getcwd (NULL, 0);
108      if (cwd == NULL)
109        fail = 4; /* getcwd didn't assert, but it failed for a long name
110                     where the answer could have been learned.  */
111      free (cwd);
112
113      /* Call rmdir first, in case the above chdir failed.  */
114      rmdir (dir_name);
115      while (0 < d--)
116        {
117          if (chdir ("..") < 0)
118            {
119              fail = 5;
120              break;
121            }
122          rmdir (dir_name);
123        }
124    }
125
126  return fail;
127}
128          ]])],
129       [gl_cv_func_getcwd_succeeds_beyond_4k=yes],
130       [dnl An abort will provoke an exit code of something like 134 (128 + 6).
131        dnl An exit code of 4 can also occur (in OpenBSD 6.7, NetBSD 5.1 for
132        dnl example): getcwd (NULL, 0) fails rather than returning a string
133        dnl longer than PATH_MAX.  This may be POSIX compliant (in some
134        dnl interpretations of POSIX).  But gnulib's getcwd module wants to
135        dnl provide a non-NULL value in this case.
136        ret=$?
137        if test $ret -ge 128 || test $ret = 4; then
138          gl_cv_func_getcwd_succeeds_beyond_4k=no
139        else
140          gl_cv_func_getcwd_succeeds_beyond_4k=yes
141        fi
142       ],
143       [case "$host_os" in
144                   # Guess yes on musl systems.
145          *-musl*) gl_cv_func_getcwd_succeeds_beyond_4k="guessing yes" ;;
146                   # Guess no otherwise, even on glibc systems.
147          *)       gl_cv_func_getcwd_succeeds_beyond_4k="guessing no"
148        esac
149       ])
150    ])
151  case "$gl_cv_func_getcwd_succeeds_beyond_4k" in
152    *no)
153      $1
154      ;;
155    *)
156      $2
157      ;;
158  esac
159])
160