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