truncate: New module.
[gnulib/ericb.git] / tests / test-getcwd.c
blobe6e0f27b55ab07d7bcd3d7e8cfe1ef5d9ca2f866
1 /* Test of getcwd() function.
2 Copyright (C) 2009-2017 Free Software Foundation, Inc.
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.
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.
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/>. */
17 #include <config.h>
19 #include <unistd.h>
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>
29 #include "pathmax.h"
30 #include "macros.h"
32 #if ! HAVE_GETPAGESIZE
33 # define getpagesize() 0
34 #endif
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)
41 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
42 # define HAVE_OPENAT_SUPPORT 1
43 #else
44 # define HAVE_OPENAT_SUPPORT 0
45 #endif
47 /* Keep this test in sync with m4/getcwd-abort-bug.m4. */
48 static int
49 test_abort_bug (void)
51 char *cwd;
52 size_t initial_cwd_len;
53 int fail = 0;
55 /* The bug is triggered when PATH_MAX < getpagesize (), so skip
56 this relatively expensive and invasive test if that's not true. */
57 #ifdef PATH_MAX
58 int bug_possible = PATH_MAX < getpagesize ();
59 #else
60 int bug_possible = 0;
61 #endif
62 if (! bug_possible)
63 return 0;
65 cwd = getcwd (NULL, 0);
66 if (cwd == NULL)
67 return 2;
69 initial_cwd_len = strlen (cwd);
70 free (cwd);
72 if (HAVE_OPENAT_SUPPORT)
74 static char const dir_name[] = "confdir-14B---";
75 size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
76 / sizeof dir_name);
77 size_t d;
78 for (d = 0; d < desired_depth; d++)
80 if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
82 if (! (errno == ERANGE || errno == ENAMETOOLONG
83 || errno == ENOENT))
84 fail = 3; /* Unable to construct deep hierarchy. */
85 break;
89 /* If libc has the bug in question, this invocation of getcwd
90 results in a failed assertion. */
91 cwd = getcwd (NULL, 0);
92 if (cwd == NULL)
93 fail = 4; /* getcwd didn't assert, but it failed for a long name
94 where the answer could have been learned. */
95 free (cwd);
97 /* Call rmdir first, in case the above chdir failed. */
98 rmdir (dir_name);
99 while (0 < d--)
101 if (chdir ("..") < 0)
103 fail = 5;
104 break;
106 rmdir (dir_name);
110 return fail;
113 /* The length of this name must be 8. */
114 #define DIR_NAME "confdir3"
115 #define DIR_NAME_LEN 8
116 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
118 /* The length of "../". */
119 #define DOTDOTSLASH_LEN 3
121 /* Leftover bytes in the buffer, to work around library or OS bugs. */
122 #define BUF_SLOP 20
124 /* Keep this test in sync with m4/getcwd-path-max.m4. */
125 static int
126 test_long_name (void)
128 #ifndef PATH_MAX
129 /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
130 at least not on a local file system. And if we were to start worrying
131 about remote file systems, we'd have to enable the wrapper function
132 all of the time, just to be safe. That's not worth the cost. */
133 return 0;
134 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
135 - DIR_NAME_SIZE - BUF_SLOP) \
136 <= PATH_MAX)
137 /* FIXME: Assuming there's a system for which this is true,
138 this should be done in a compile test. */
139 return 0;
140 #else
141 char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
142 + DIR_NAME_SIZE + BUF_SLOP];
143 char *cwd = getcwd (buf, PATH_MAX);
144 size_t initial_cwd_len;
145 size_t cwd_len;
146 int fail = 0;
147 size_t n_chdirs = 0;
149 if (cwd == NULL)
150 return 1;
152 cwd_len = initial_cwd_len = strlen (cwd);
154 while (1)
156 # ifdef HAVE_GETCWD_SHORTER
157 /* On OS/X <= 10.9 for example, we're restricted to shorter paths
158 as lstat() doesn't support more than PATH_MAX. */
159 size_t dotdot_max = PATH_MAX * 2;
160 # else
161 size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
162 # endif
163 char *c = NULL;
165 cwd_len += DIR_NAME_SIZE;
166 /* If mkdir or chdir fails, it could be that this system cannot create
167 any file with an absolute name longer than PATH_MAX, such as cygwin.
168 If so, leave fail as 0, because the current working directory can't
169 be too long for getcwd if it can't even be created. For other
170 errors, be pessimistic and consider that as a failure, too. */
171 if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
173 if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
174 fail = 2;
175 break;
178 if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
180 c = getcwd (buf, PATH_MAX);
181 if (!c && errno == ENOENT)
183 fail = 3;
184 break;
186 if (c)
188 fail = 4;
189 break;
191 if (! (errno == ERANGE || errno == ENAMETOOLONG))
193 fail = 5;
194 break;
198 if (dotdot_max <= cwd_len - initial_cwd_len)
200 if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
201 break;
202 c = getcwd (buf, cwd_len + 1);
203 if (!c)
205 if (! (errno == ERANGE || errno == ENOENT
206 || errno == ENAMETOOLONG))
208 fail = 6;
209 break;
211 if (HAVE_OPENAT_SUPPORT || errno == ERANGE || errno == ENOENT)
213 fail = 7;
214 break;
219 if (c && strlen (c) != cwd_len)
221 fail = 8;
222 break;
224 ++n_chdirs;
227 /* Leaving behind such a deep directory is not polite.
228 So clean up here, right away, even though the driving
229 shell script would also clean up. */
231 size_t i;
233 /* Try rmdir first, in case the chdir failed. */
234 rmdir (DIR_NAME);
235 for (i = 0; i <= n_chdirs; i++)
237 if (chdir ("..") < 0)
238 break;
239 if (rmdir (DIR_NAME) != 0)
240 break;
244 return fail;
245 #endif
249 main (int argc, char **argv)
251 return test_abort_bug () * 10 + test_long_name ();