getcwd: fix test failures on mingw
[gnulib.git] / tests / test-getcwd.c
1 /* Test of getcwd() function.
2    Copyright (C) 2009-2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 #include <config.h>
18
19 #include <unistd.h>
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28
29 #include "pathmax.h"
30 #include "macros.h"
31
32 #if ! HAVE_GETPAGESIZE
33 # define getpagesize() 0
34 #endif
35
36 /* This size is chosen to be larger than PATH_MAX (4k), yet smaller than
37    the 16kB pagesize on ia64 linux.  Those conditions make the code below
38    trigger a bug in glibc's getcwd implementation before 2.4.90-10.  */
39 #define TARGET_LEN (5 * 1024)
40
41 /* Keep this test in sync with m4/getcwd-abort-bug.m4.  */
42 static int
43 test_abort_bug (void)
44 {
45   char const *dir_name = "confdir-14B---";
46   char *cwd;
47   size_t initial_cwd_len;
48   int fail = 0;
49   size_t desired_depth;
50   size_t d;
51
52 #ifdef PATH_MAX
53   /* The bug is triggered when PATH_MAX < getpagesize (), so skip
54      this relatively expensive and invasive test if that's not true.  */
55   if (getpagesize () <= PATH_MAX)
56     return 0;
57 #endif
58
59   cwd = getcwd (NULL, 0);
60   if (cwd == NULL)
61     return 2;
62
63   initial_cwd_len = strlen (cwd);
64   free (cwd);
65   desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
66                    / (1 + strlen (dir_name)));
67   for (d = 0; d < desired_depth; d++)
68     {
69       if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
70         {
71           if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
72             fail = 3; /* Unable to construct deep hierarchy.  */
73           break;
74         }
75     }
76
77   /* If libc has the bug in question, this invocation of getcwd
78      results in a failed assertion.  */
79   cwd = getcwd (NULL, 0);
80   if (cwd == NULL)
81     fail = 4; /* getcwd didn't assert, but it failed for a long name
82                  where the answer could have been learned.  */
83   free (cwd);
84
85   /* Call rmdir first, in case the above chdir failed.  */
86   rmdir (dir_name);
87   while (0 < d--)
88     {
89       if (chdir ("..") < 0)
90         {
91           fail = 5;
92           break;
93         }
94       rmdir (dir_name);
95     }
96
97   return fail;
98 }
99
100 /* The length of this name must be 8.  */
101 #define DIR_NAME "confdir3"
102 #define DIR_NAME_LEN 8
103 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
104
105 /* The length of "../".  */
106 #define DOTDOTSLASH_LEN 3
107
108 /* Leftover bytes in the buffer, to work around library or OS bugs.  */
109 #define BUF_SLOP 20
110
111 /* Keep this test in sync with m4/getcwd-path-max.m4.  */
112 static int
113 test_long_name (void)
114 {
115 #ifndef PATH_MAX
116   /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
117      at least not on a local file system.  And if we were to start worrying
118      about remote file systems, we'd have to enable the wrapper function
119      all of the time, just to be safe.  That's not worth the cost.  */
120   return 0;
121 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
122         - DIR_NAME_SIZE - BUF_SLOP) \
123        <= PATH_MAX)
124   /* FIXME: Assuming there's a system for which this is true,
125      this should be done in a compile test.  */
126   return 0;
127 #else
128   char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
129            + DIR_NAME_SIZE + BUF_SLOP];
130   char *cwd = getcwd (buf, PATH_MAX);
131   size_t initial_cwd_len;
132   size_t cwd_len;
133   int fail = 0;
134   size_t n_chdirs = 0;
135
136   if (cwd == NULL)
137     return 10;
138
139   cwd_len = initial_cwd_len = strlen (cwd);
140
141   while (1)
142     {
143       size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
144       char *c = NULL;
145
146       cwd_len += DIR_NAME_SIZE;
147       /* If mkdir or chdir fails, it could be that this system cannot create
148          any file with an absolute name longer than PATH_MAX, such as cygwin.
149          If so, leave fail as 0, because the current working directory can't
150          be too long for getcwd if it can't even be created.  For other
151          errors, be pessimistic and consider that as a failure, too.  */
152       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
153         {
154           if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
155             fail = 20;
156           break;
157         }
158
159       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
160         {
161           c = getcwd (buf, PATH_MAX);
162           if (!c && errno == ENOENT)
163             {
164               fail = 11;
165               break;
166             }
167           if (c || ! (errno == ERANGE || errno == ENAMETOOLONG))
168             {
169               fail = 21;
170               break;
171             }
172         }
173
174       if (dotdot_max <= cwd_len - initial_cwd_len)
175         {
176           if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
177             break;
178           c = getcwd (buf, cwd_len + 1);
179           if (!c)
180             {
181               if (! (errno == ERANGE || errno == ENOENT
182                      || errno == ENAMETOOLONG))
183                 {
184                   fail = 22;
185                   break;
186                 }
187               if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
188                 {
189                   fail = 12;
190                   break;
191                 }
192             }
193         }
194
195       if (c && strlen (c) != cwd_len)
196         {
197           fail = 23;
198           break;
199         }
200       ++n_chdirs;
201     }
202
203   /* Leaving behind such a deep directory is not polite.
204      So clean up here, right away, even though the driving
205      shell script would also clean up.  */
206   {
207     size_t i;
208
209     /* Try rmdir first, in case the chdir failed.  */
210     rmdir (DIR_NAME);
211     for (i = 0; i <= n_chdirs; i++)
212       {
213         if (chdir ("..") < 0)
214           break;
215         if (rmdir (DIR_NAME) != 0)
216           break;
217       }
218   }
219
220   return fail;
221 #endif
222 }
223
224 int
225 main (int argc, char **argv)
226 {
227   return test_abort_bug () + test_long_name ();
228 }