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