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