NEWS.stable: log cherry-pick [a5a35f0]->[c4596b1] freopen: Documentation.
[gnulib.git] / m4 / getcwd-path-max.m4
1 # serial 16
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-2011 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([gl_USE_SYSTEM_EXTENSIONS])
20   AC_CACHE_CHECK([whether getcwd handles long file names properly],
21     gl_cv_func_getcwd_path_max,
22     [# Arrange for deletion of the temporary directory this test creates.
23      ac_clean_files="$ac_clean_files confdir3"
24      dnl Please keep this in sync with tests/test-getcwd.c.
25      AC_RUN_IFELSE(
26        [AC_LANG_SOURCE(
27           [[
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <limits.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <fcntl.h>
36
37 #ifndef AT_FDCWD
38 # define AT_FDCWD 0
39 #endif
40 #ifdef ENAMETOOLONG
41 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
42 #else
43 # define is_ENAMETOOLONG(x) 0
44 #endif
45
46 /* Don't get link errors because mkdir is redefined to rpl_mkdir.  */
47 #undef mkdir
48
49 #ifndef S_IRWXU
50 # define S_IRWXU 0700
51 #endif
52
53 /* The length of this name must be 8.  */
54 #define DIR_NAME "confdir3"
55 #define DIR_NAME_LEN 8
56 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
57
58 /* The length of "../".  */
59 #define DOTDOTSLASH_LEN 3
60
61 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
62 #define BUF_SLOP 20
63
64 int
65 main ()
66 {
67 #ifndef PATH_MAX
68   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
69      at least not on a local file system.  And if we were to start worrying
70      about remote file systems, we'd have to enable the wrapper function
71      all of the time, just to be safe.  That's not worth the cost.  */
72   exit (0);
73 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
74         - DIR_NAME_SIZE - BUF_SLOP) \
75        <= PATH_MAX)
76   /* FIXME: Assuming there's a system for which this is true,
77      this should be done in a compile test.  */
78   exit (0);
79 #else
80   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
81            + DIR_NAME_SIZE + BUF_SLOP];
82   char *cwd = getcwd (buf, PATH_MAX);
83   size_t initial_cwd_len;
84   size_t cwd_len;
85   int fail = 0;
86   size_t n_chdirs = 0;
87
88   if (cwd == NULL)
89     exit (10);
90
91   cwd_len = initial_cwd_len = strlen (cwd);
92
93   while (1)
94     {
95       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
96       char *c = NULL;
97
98       cwd_len += DIR_NAME_SIZE;
99       /* If mkdir or chdir fails, it could be that this system cannot create
100          any file with an absolute name longer than PATH_MAX, such as cygwin.
101          If so, leave fail as 0, because the current working directory can't
102          be too long for getcwd if it can't even be created.  For other
103          errors, be pessimistic and consider that as a failure, too.  */
104       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
105         {
106           if (! (errno == ERANGE || is_ENAMETOOLONG (errno)))
107             fail = 20;
108           break;
109         }
110
111       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
112         {
113           c = getcwd (buf, PATH_MAX);
114           if (!c && errno == ENOENT)
115             {
116               fail = 11;
117               break;
118             }
119           if (c || ! (errno == ERANGE || is_ENAMETOOLONG (errno)))
120             {
121               fail = 21;
122               break;
123             }
124         }
125
126       if (dotdot_max <= cwd_len - initial_cwd_len)
127         {
128           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
129             break;
130           c = getcwd (buf, cwd_len + 1);
131           if (!c)
132             {
133               if (! (errno == ERANGE || errno == ENOENT
134                      || is_ENAMETOOLONG (errno)))
135                 {
136                   fail = 22;
137                   break;
138                 }
139               if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
140                 {
141                   fail = 12;
142                   break;
143                 }
144             }
145         }
146
147       if (c && strlen (c) != cwd_len)
148         {
149           fail = 23;
150           break;
151         }
152       ++n_chdirs;
153     }
154
155   /* Leaving behind such a deep directory is not polite.
156      So clean up here, right away, even though the driving
157      shell script would also clean up.  */
158   {
159     size_t i;
160
161     /* Try rmdir first, in case the chdir failed.  */
162     rmdir (DIR_NAME);
163     for (i = 0; i <= n_chdirs; i++)
164       {
165         if (chdir ("..") < 0)
166           break;
167         if (rmdir (DIR_NAME) != 0)
168           break;
169       }
170   }
171
172   exit (fail);
173 #endif
174 }
175           ]])],
176     [gl_cv_func_getcwd_path_max=yes],
177     [case $? in
178      10|11|12) gl_cv_func_getcwd_path_max='no, but it is partly working';;
179      *) gl_cv_func_getcwd_path_max=no;;
180      esac],
181     [gl_cv_func_getcwd_path_max=no])
182   ])
183   case $gl_cv_func_getcwd_path_max in
184   no,*)
185     AC_DEFINE([HAVE_PARTLY_WORKING_GETCWD], [1],
186       [Define to 1 if getcwd works, except it sometimes fails when it shouldn't,
187        setting errno to ERANGE, ENAMETOOLONG, or ENOENT.]);;
188   esac
189 ])