1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
# getcwd-path-max.m4
# serial 26
dnl Copyright (C) 2003-2007, 2009-2025 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl This file is offered as-is, without any warranty.
# Check for several getcwd bugs with long file names.
# If so, arrange to compile the wrapper function.
# This is necessary for at least GNU libc on linux-2.4.19 and 2.4.20.
# I've heard that this is due to a Linux kernel bug, and that it has
# been fixed between 2.4.21-pre3 and 2.4.21-pre4.
# From Jim Meyering
AC_DEFUN([gl_FUNC_GETCWD_PATH_MAX],
[
AC_CHECK_DECLS_ONCE([getcwd, alarm])
AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
AC_CHECK_HEADERS_ONCE([unistd.h])
AC_REQUIRE([gl_PATHMAX_SNIPPET_PREREQ])
AC_CACHE_CHECK([whether getcwd handles long file names properly],
[gl_cv_func_getcwd_path_max],
[# Arrange for deletion of the temporary directory this test creates.
ac_clean_files="$ac_clean_files confdir3"
dnl Please keep this in sync with tests/test-getcwd.c.
AC_RUN_IFELSE(
[AC_LANG_SOURCE(
[[
#include <errno.h>
#include <stdlib.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#else
# include <direct.h>
#endif
#if HAVE_DECL_ALARM
# include <signal.h>
#endif
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
]gl_PATHMAX_SNIPPET[
#ifndef AT_FDCWD
# define AT_FDCWD 0
#endif
#ifdef ENAMETOOLONG
# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
#else
# define is_ENAMETOOLONG(x) 0
#endif
/* Use the getcwd function, not any macro. */
#undef getcwd
]GL_MDA_DEFINES[
#ifndef S_IRWXU
# define S_IRWXU 0700
#endif
/* The length of this name must be 8. */
#define DIR_NAME "confdir3"
#define DIR_NAME_LEN 8
#define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
/* The length of "../". */
#define DOTDOTSLASH_LEN 3
/* Leftover bytes in the buffer, to work around library or OS bugs. */
#define BUF_SLOP 20
int
main ()
{
#ifndef PATH_MAX
/* The Hurd doesn't define this, so getcwd can't exhibit the bug --
at least not on a local file system. And if we were to start worrying
about remote file systems, we'd have to enable the wrapper function
all of the time, just to be safe. That's not worth the cost. */
exit (0);
#elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
- DIR_NAME_SIZE - BUF_SLOP) \
<= PATH_MAX)
/* FIXME: Assuming there's a system for which this is true,
this should be done in a compile test. */
exit (0);
#else
char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
+ DIR_NAME_SIZE + BUF_SLOP];
char *cwd;
size_t initial_cwd_len;
size_t cwd_len;
int fail;
size_t n_chdirs;
# if HAVE_DECL_ALARM
/* This test makes some buggy getcwd implementations take a long time, e.g.
on NAS devices
<https://lists.gnu.org/archive/html/bug-gnulib/2024-03/msg00444.html>
and in sandboxed environments <https://bugs.gentoo.org/447970>.
Give up after 5 seconds; a getcwd slower than that isn't worth using
anyway. */
signal (SIGALRM, SIG_DFL);
alarm (5);
# endif
cwd = getcwd (buf, PATH_MAX);
if (cwd == NULL)
exit (10);
cwd_len = initial_cwd_len = strlen (cwd);
fail = 0;
n_chdirs = 0;
while (1)
{
size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
char *c = NULL;
cwd_len += DIR_NAME_SIZE;
/* If mkdir or chdir fails, it could be that this system cannot create
any file with an absolute name longer than PATH_MAX, such as cygwin.
If so, leave fail as 0, because the current working directory can't
be too long for getcwd if it can't even be created. On Linux with
the 9p file system, mkdir fails with error EINVAL when cwd_len gets
too long; ignore this failure because the getcwd() system call
produces good results whereas the gnulib substitute calls getdents64
which fails with error EPROTO.
For other errors, be pessimistic and consider that as a failure,
too. */
if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
{
if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
#ifdef __linux__
if (! (errno == EINVAL))
#endif
fail = 20;
break;
}
if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
{
struct stat sb;
c = getcwd (buf, PATH_MAX);
if (!c && errno == ENOENT)
{
fail = 11;
break;
}
if (c)
{
fail = 31;
break;
}
if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
{
fail = 21;
break;
}
/* Our replacement needs to be able to stat() long ../../paths,
so generate a path larger than PATH_MAX to check,
avoiding the replacement if we can't stat(). */
c = getcwd (buf, cwd_len + 1);
if (c && !AT_FDCWD && stat (c, &sb) != 0 && is_ENAMETOOLONG (errno))
{
fail = 32;
break;
}
}
if (dotdot_max <= cwd_len - initial_cwd_len)
{
if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
break;
c = getcwd (buf, cwd_len + 1);
if (!c)
{
if (! (errno == ERANGE || errno == ENOENT
|| is_ENAMETOOLONG (errno)))
{
fail = 22;
break;
}
if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
{
fail = 12;
break;
}
}
}
if (c && strlen (c) != cwd_len)
{
fail = 23;
break;
}
++n_chdirs;
}
/* Leaving behind such a deep directory is not polite.
So clean up here, right away, even though the driving
shell script would also clean up. */
{
size_t i;
/* Try rmdir first, in case the chdir failed. */
rmdir (DIR_NAME);
for (i = 0; i <= n_chdirs; i++)
{
if (chdir ("..") < 0)
break;
if (rmdir (DIR_NAME) != 0)
break;
}
}
exit (fail);
#endif
}
]])],
[gl_cv_func_getcwd_path_max=yes],
[case $? in
10|11|12) gl_cv_func_getcwd_path_max='no, but it is partly working';;
31) gl_cv_func_getcwd_path_max='no, it has the AIX bug';;
32) gl_cv_func_getcwd_path_max='yes, but with shorter paths';;
*) gl_cv_func_getcwd_path_max=no;;
esac],
[# Cross-compilation guesses:
case "$host_os" in
aix*) # On AIX, it has the AIX bug.
gl_cv_func_getcwd_path_max='guessing no, it has the AIX bug' ;;
gnu*) # On Hurd, it is 'yes'.
gl_cv_func_getcwd_path_max='guessing yes' ;;
linux* | kfreebsd*)
# On older Linux+glibc it's 'no, but it is partly working',
# on newer Linux+glibc it's 'yes'.
# On Linux+musl libc, it's 'no, but it is partly working'.
# On kFreeBSD+glibc, it's 'no, but it is partly working'.
gl_cv_func_getcwd_path_max='guessing no, but it is partly working' ;;
*) # If we don't know, obey --enable-cross-guesses.
gl_cv_func_getcwd_path_max="$gl_cross_guess_normal" ;;
esac
])
])
])
|