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