68975617633702091f54bea06ddc2316f277b500
[gnulib.git] / m4 / getcwd-path-max.m4
1 # serial 19
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-2012 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 /* Don't get link errors because mkdir is redefined to rpl_mkdir.  */
56 #undef mkdir
57
58 #ifndef S_IRWXU
59 # define S_IRWXU 0700
60 #endif
61
62 /* The length of this name must be 8.  */
63 #define DIR_NAME "confdir3"
64 #define DIR_NAME_LEN 8
65 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
66
67 /* The length of "../".  */
68 #define DOTDOTSLASH_LEN 3
69
70 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
71 #define BUF_SLOP 20
72
73 int
74 main ()
75 {
76 #ifndef PATH_MAX
77   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
78      at least not on a local file system.  And if we were to start worrying
79      about remote file systems, we'd have to enable the wrapper function
80      all of the time, just to be safe.  That's not worth the cost.  */
81   exit (0);
82 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
83         - DIR_NAME_SIZE - BUF_SLOP) \
84        <= PATH_MAX)
85   /* FIXME: Assuming there's a system for which this is true,
86      this should be done in a compile test.  */
87   exit (0);
88 #else
89   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
90            + DIR_NAME_SIZE + BUF_SLOP];
91   char *cwd = getcwd (buf, PATH_MAX);
92   size_t initial_cwd_len;
93   size_t cwd_len;
94   int fail = 0;
95   size_t n_chdirs = 0;
96
97   if (cwd == NULL)
98     exit (10);
99
100   cwd_len = initial_cwd_len = strlen (cwd);
101
102   while (1)
103     {
104       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
105       char *c = NULL;
106
107       cwd_len += DIR_NAME_SIZE;
108       /* If mkdir or chdir fails, it could be that this system cannot create
109          any file with an absolute name longer than PATH_MAX, such as cygwin.
110          If so, leave fail as 0, because the current working directory can't
111          be too long for getcwd if it can't even be created.  For other
112          errors, be pessimistic and consider that as a failure, too.  */
113       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
114         {
115           if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
116             fail = 20;
117           break;
118         }
119
120       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
121         {
122           c = getcwd (buf, PATH_MAX);
123           if (!c && errno == ENOENT)
124             {
125               fail = 11;
126               break;
127             }
128           if (c)
129             {
130               fail = 31;
131               break;
132             }
133           if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
134             {
135               fail = 21;
136               break;
137             }
138         }
139
140       if (dotdot_max <= cwd_len - initial_cwd_len)
141         {
142           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
143             break;
144           c = getcwd (buf, cwd_len + 1);
145           if (!c)
146             {
147               if (! (errno == ERANGE || errno == ENOENT
148                      || is_ENAMETOOLONG (errno)))
149                 {
150                   fail = 22;
151                   break;
152                 }
153               if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
154                 {
155                   fail = 12;
156                   break;
157                 }
158             }
159         }
160
161       if (c && strlen (c) != cwd_len)
162         {
163           fail = 23;
164           break;
165         }
166       ++n_chdirs;
167     }
168
169   /* Leaving behind such a deep directory is not polite.
170      So clean up here, right away, even though the driving
171      shell script would also clean up.  */
172   {
173     size_t i;
174
175     /* Try rmdir first, in case the chdir failed.  */
176     rmdir (DIR_NAME);
177     for (i = 0; i <= n_chdirs; i++)
178       {
179         if (chdir ("..") < 0)
180           break;
181         if (rmdir (DIR_NAME) != 0)
182           break;
183       }
184   }
185
186   exit (fail);
187 #endif
188 }
189           ]])],
190     [gl_cv_func_getcwd_path_max=yes],
191     [case $? in
192      10|11|12) gl_cv_func_getcwd_path_max='no, but it is partly working';;
193      31) gl_cv_func_getcwd_path_max='no, it has the AIX bug';;
194      *) gl_cv_func_getcwd_path_max=no;;
195      esac],
196     [case "$host_os" in
197        aix*) gl_cv_func_getcwd_path_max='no, it has the AIX bug';;
198        *) gl_cv_func_getcwd_path_max=no;;
199      esac])
200   ])
201 ])