Use an all-permissive copyright notice, recommended by RMS.
[gnulib.git] / m4 / getcwd-path-max.m4
1 #serial 7
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 # 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, be pessimistic and consider that
102          as a failure, too.  */
103       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
104         {
105           fail = 2;
106           break;
107         }
108
109       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
110         {
111           c = getcwd (buf, PATH_MAX);
112           if (!c && errno == ENOENT)
113             {
114               fail = 1;
115               break;
116             }
117           if (c || ! (errno == ERANGE || is_ENAMETOOLONG (errno)))
118             {
119               fail = 2;
120               break;
121             }
122         }
123
124       if (dotdot_max <= cwd_len - initial_cwd_len)
125         {
126           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
127             break;
128           c = getcwd (buf, cwd_len + 1);
129           if (!c)
130             {
131               if (! (errno == ERANGE || errno == ENOENT
132                      || is_ENAMETOOLONG (errno)))
133                 {
134                   fail = 2;
135                   break;
136                 }
137               if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
138                 {
139                   fail = 1;
140                   break;
141                 }
142             }
143         }
144
145       if (c && strlen (c) != cwd_len)
146         {
147           fail = 2;
148           break;
149         }
150       ++n_chdirs;
151     }
152
153   /* Leaving behind such a deep directory is not polite.
154      So clean up here, right away, even though the driving
155      shell script would also clean up.  */
156   {
157     size_t i;
158
159     /* Unlink first, in case the chdir failed.  */
160     unlink (DIR_NAME);
161     for (i = 0; i <= n_chdirs; i++)
162       {
163         if (chdir ("..") < 0)
164           break;
165         rmdir (DIR_NAME);
166       }
167   }
168
169   exit (fail);
170 #endif
171 }
172           ]])],
173     [gl_cv_func_getcwd_path_max=yes],
174     [case $? in
175      1) gl_cv_func_getcwd_path_max='no, but it is partly working';;
176      *) gl_cv_func_getcwd_path_max=no;;
177      esac],
178     [gl_cv_func_getcwd_path_max=no])
179   ])
180   case $gl_cv_func_getcwd_path_max in
181   no,*)
182     AC_DEFINE([HAVE_PARTLY_WORKING_GETCWD], 1,
183       [Define to 1 if getcwd works, except it sometimes fails when it shouldn't,
184        setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If __GETCWD_PREFIX
185        is not defined, it doesn't matter whether HAVE_PARTLY_WORKING_GETCWD
186        is defined.]);;
187   esac
188 ])