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