(gl_FUNC_GETCWD_PATH_MAX):
[gnulib.git] / m4 / getcwd-path-max.m4
1 #serial 6
2 # Check for several getcwd bugs with long paths.
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, 2004 Free Software Foundation, Inc.
10
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2, or (at your option)
14 # any later version.
15
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software Foundation,
23 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25 # From Jim Meyering
26
27 AC_DEFUN([gl_FUNC_GETCWD_PATH_MAX],
28 [
29   AC_CHECK_DECLS_ONCE(getcwd)
30   AC_CHECK_HEADERS_ONCE(fcntl.h)
31   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
32   AC_CACHE_CHECK([whether getcwd handles long file names properly],
33     gl_cv_func_getcwd_path_max,
34     [# Arrange for deletion of the temporary directory this test creates.
35      ac_clean_files="$ac_clean_files confdir3"
36      AC_RUN_IFELSE(
37        [AC_LANG_SOURCE(
38           [[
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <limits.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #if HAVE_FCNTL_H
47 # include <fcntl.h>
48 #endif
49
50 #ifndef AT_FDCWD
51 # define AT_FDCWD 0
52 #endif
53 #ifdef ENAMETOOLONG
54 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
55 #else
56 # define is_ENAMETOOLONG(x) 0
57 #endif
58
59 /* Don't get link errors because mkdir is redefined to rpl_mkdir.  */
60 #undef mkdir
61
62 #ifndef S_IRWXU
63 # define S_IRWXU 0700
64 #endif
65
66 /* The length of this name must be 8.  */
67 #define DIR_NAME "confdir3"
68 #define DIR_NAME_LEN 8
69 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
70
71 /* The length of "../".  */
72 #define DOTDOTSLASH_LEN 3
73
74 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
75 #define BUF_SLOP 20
76
77 int
78 main (void)
79 {
80 #ifndef PATH_MAX
81   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
82      at least not on a local file system.  And if we were to start worrying
83      about remote file systems, we'd have to enable the wrapper function
84      all of the time, just to be safe.  That's not worth the cost.  */
85   exit (0);
86 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
87         - DIR_NAME_SIZE - BUF_SLOP) \
88        <= PATH_MAX)
89   /* FIXME: Assuming there's a system for which this is true,
90      this should be done in a compile test.  */
91   exit (0);
92 #else
93   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
94            + DIR_NAME_SIZE + BUF_SLOP];
95   char *cwd = getcwd (buf, PATH_MAX);
96   size_t initial_cwd_len;
97   size_t cwd_len;
98   int fail = 0;
99   size_t n_chdirs = 0;
100
101   if (cwd == NULL)
102     exit (1);
103
104   cwd_len = initial_cwd_len = strlen (cwd);
105
106   while (1)
107     {
108       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
109       char *c = NULL;
110
111       cwd_len += DIR_NAME_SIZE;
112       /* If mkdir or chdir fails, be pessimistic and consider that
113          as a failure, too.  */
114       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
115         {
116           fail = 2;
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 = 1;
126               break;
127             }
128           if (c || ! (errno == ERANGE || is_ENAMETOOLONG (errno)))
129             {
130               fail = 2;
131               break;
132             }
133         }
134
135       if (dotdot_max <= cwd_len - initial_cwd_len)
136         {
137           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
138             break;
139           c = getcwd (buf, cwd_len + 1);
140           if (!c)
141             {
142               if (! (errno == ERANGE || errno == ENOENT
143                      || is_ENAMETOOLONG (errno)))
144                 {
145                   fail = 2;
146                   break;
147                 }
148               if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
149                 {
150                   fail = 1;
151                   break;
152                 }
153             }
154         }
155
156       if (c && strlen (c) != cwd_len)
157         {
158           fail = 2;
159           break;
160         }
161       ++n_chdirs;
162     }
163
164   /* Leaving behind such a deep directory is not polite.
165      So clean up here, right away, even though the driving
166      shell script would also clean up.  */
167   {
168     size_t i;
169
170     /* Unlink first, in case the chdir failed.  */
171     unlink (DIR_NAME);
172     for (i = 0; i <= n_chdirs; i++)
173       {
174         if (chdir ("..") < 0)
175           break;
176         rmdir (DIR_NAME);
177       }
178   }
179
180   exit (fail);
181 #endif
182 }
183           ]])],
184     [gl_cv_func_getcwd_path_max=yes],
185     [case $? in
186      1) 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.  If __GETCWD_PREFIX
196        is not defined, it doesn't matter whether HAVE_PARTLY_WORKING_GETCWD
197        is defined.]);;
198   esac
199 ])