2 # Check for several getcwd bugs with long file names.
3 # If so, arrange to compile the wrapper function.
5 # This is necessary for at least GNU libc on linux-2.4.19 and 2.4.20.
6 # I've heard that this is due to a Linux kernel bug, and that it has
7 # been fixed between 2.4.21-pre3 and 2.4.21-pre4.
9 # Copyright (C) 2003-2007, 2009-2020 Free Software Foundation, Inc.
10 # This file is free software; the Free Software Foundation
11 # gives unlimited permission to copy and/or distribute it,
12 # with or without modifications, as long as this notice is preserved.
16 AC_DEFUN([gl_FUNC_GETCWD_PATH_MAX],
18 AC_CHECK_DECLS_ONCE([getcwd])
19 AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
20 AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
21 AC_CHECK_HEADERS_ONCE([unistd.h])
22 AC_REQUIRE([gl_PATHMAX_SNIPPET_PREREQ])
23 AC_CACHE_CHECK([whether getcwd handles long file names properly],
24 [gl_cv_func_getcwd_path_max],
25 [# Arrange for deletion of the temporary directory this test creates.
26 ac_clean_files="$ac_clean_files confdir3"
27 dnl Please keep this in sync with tests/test-getcwd.c.
41 #include <sys/types.h>
50 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
52 # define is_ENAMETOOLONG(x) 0
55 /* Use the getcwd function, not any macro. */
58 /* Don't get link errors because mkdir is redefined to rpl_mkdir. */
65 /* The length of this name must be 8. */
66 #define DIR_NAME "confdir3"
67 #define DIR_NAME_LEN 8
68 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
70 /* The length of "../". */
71 #define DOTDOTSLASH_LEN 3
73 /* Leftover bytes in the buffer, to work around library or OS bugs. */
80 /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
81 at least not on a local file system. And if we were to start worrying
82 about remote file systems, we'd have to enable the wrapper function
83 all of the time, just to be safe. That's not worth the cost. */
85 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
86 - DIR_NAME_SIZE - BUF_SLOP) \
88 /* FIXME: Assuming there's a system for which this is true,
89 this should be done in a compile test. */
92 char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
93 + DIR_NAME_SIZE + BUF_SLOP];
94 char *cwd = getcwd (buf, PATH_MAX);
95 size_t initial_cwd_len;
103 cwd_len = initial_cwd_len = strlen (cwd);
107 size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
110 cwd_len += DIR_NAME_SIZE;
111 /* If mkdir or chdir fails, it could be that this system cannot create
112 any file with an absolute name longer than PATH_MAX, such as cygwin.
113 If so, leave fail as 0, because the current working directory can't
114 be too long for getcwd if it can't even be created. On Linux with
115 the 9p file system, mkdir fails with error EINVAL when cwd_len gets
116 too long; ignore this failure because the getcwd() system call
117 produces good results whereas the gnulib substitute calls getdents64
118 which fails with error EPROTO.
119 For other errors, be pessimistic and consider that as a failure,
121 if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
123 if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
125 if (! (errno == EINVAL))
131 if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
135 c = getcwd (buf, PATH_MAX);
136 if (!c && errno == ENOENT)
146 if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
152 /* Our replacement needs to be able to stat() long ../../paths,
153 so generate a path larger than PATH_MAX to check,
154 avoiding the replacement if we can't stat(). */
155 c = getcwd (buf, cwd_len + 1);
156 if (c && !AT_FDCWD && stat (c, &sb) != 0 && is_ENAMETOOLONG (errno))
163 if (dotdot_max <= cwd_len - initial_cwd_len)
165 if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
167 c = getcwd (buf, cwd_len + 1);
170 if (! (errno == ERANGE || errno == ENOENT
171 || is_ENAMETOOLONG (errno)))
176 if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
184 if (c && strlen (c) != cwd_len)
192 /* Leaving behind such a deep directory is not polite.
193 So clean up here, right away, even though the driving
194 shell script would also clean up. */
198 /* Try rmdir first, in case the chdir failed. */
200 for (i = 0; i <= n_chdirs; i++)
202 if (chdir ("..") < 0)
204 if (rmdir (DIR_NAME) != 0)
213 [gl_cv_func_getcwd_path_max=yes],
215 10|11|12) gl_cv_func_getcwd_path_max='no, but it is partly working';;
216 31) gl_cv_func_getcwd_path_max='no, it has the AIX bug';;
217 32) gl_cv_func_getcwd_path_max='yes, but with shorter paths';;
218 *) gl_cv_func_getcwd_path_max=no;;
220 [# Cross-compilation guesses:
222 aix*) # On AIX, it has the AIX bug.
223 gl_cv_func_getcwd_path_max='guessing no, it has the AIX bug' ;;
224 gnu*) # On Hurd, it is 'yes'.
225 gl_cv_func_getcwd_path_max='guessing yes' ;;
227 # On older Linux+glibc it's 'no, but it is partly working',
228 # on newer Linux+glibc it's 'yes'.
229 # On Linux+musl libc, it's 'no, but it is partly working'.
230 # On kFreeBSD+glibc, it's 'no, but it is partly working'.
231 gl_cv_func_getcwd_path_max='guessing no, but it is partly working' ;;
232 *) # If we don't know, obey --enable-cross-guesses.
233 gl_cv_func_getcwd_path_max="$gl_cross_guess_normal" ;;