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