pthread-once tests: Fix crash on mingw.
[gnulib.git] / tests / test-getcwd.c
blob00447e83a844dcd231eaeed24027cc9e73d1d847
1 /* Test of getcwd() function.
2 Copyright (C) 2009-2024 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 <https://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 "qemu.h"
31 #include "macros.h"
33 #if !(HAVE_GETPAGESIZE || defined getpagesize)
34 # define getpagesize() 0
35 #endif
37 /* This size is chosen to be larger than PATH_MAX (4k), yet smaller than
38 the 16kB pagesize on ia64 linux. Those conditions make the code below
39 trigger a bug in glibc's getcwd implementation before 2.4.90-10. */
40 #define TARGET_LEN (5 * 1024)
42 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
43 # define HAVE_OPENAT_SUPPORT 1
44 #else
45 # define HAVE_OPENAT_SUPPORT 0
46 #endif
48 /* Keep this test in sync with m4/getcwd-abort-bug.m4. */
49 static int
50 test_abort_bug (void)
52 char *cwd;
53 size_t initial_cwd_len;
54 int fail = 0;
56 /* The bug is triggered when PATH_MAX < getpagesize (), so skip
57 this relatively expensive and invasive test if that's not true. */
58 #ifdef PATH_MAX
59 int bug_possible = PATH_MAX < getpagesize ();
60 #else
61 int bug_possible = 0;
62 #endif
63 if (! bug_possible)
64 return 0;
66 cwd = getcwd (NULL, 0);
67 if (cwd == NULL)
68 return 2;
70 initial_cwd_len = strlen (cwd);
71 free (cwd);
73 if (HAVE_OPENAT_SUPPORT)
75 static char const dir_name[] = "confdir-14B---";
76 size_t desired_depth = ((TARGET_LEN - 1 - initial_cwd_len)
77 / sizeof dir_name);
78 size_t d;
79 for (d = 0; d < desired_depth; d++)
81 if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0)
83 if (! (errno == ERANGE || errno == ENAMETOOLONG
84 || errno == ENOENT))
85 fail = 3; /* Unable to construct deep hierarchy. */
86 break;
90 /* If libc has the bug in question, this invocation of getcwd
91 results in a failed assertion. */
92 cwd = getcwd (NULL, 0);
93 if (cwd == NULL)
94 fail = 4; /* getcwd didn't assert, but it failed for a long name
95 where the answer could have been learned. */
96 free (cwd);
98 /* Call rmdir first, in case the above chdir failed. */
99 rmdir (dir_name);
100 while (0 < d--)
102 if (chdir ("..") < 0)
104 fail = 5;
105 break;
107 rmdir (dir_name);
111 return fail;
114 /* The length of this name must be 8. */
115 #define DIR_NAME "confdir3"
116 #define DIR_NAME_LEN 8
117 #define DIR_NAME_SIZE (DIR_NAME_LEN + 1)
119 /* The length of "../". */
120 #define DOTDOTSLASH_LEN 3
122 /* Leftover bytes in the buffer, to work around library or OS bugs. */
123 #define BUF_SLOP 20
125 /* Keep this test in sync with m4/getcwd-path-max.m4. */
126 static int
127 test_long_name (void)
129 #ifndef PATH_MAX
130 /* The Hurd doesn't define this, so getcwd can't exhibit the bug --
131 at least not on a local file system. And if we were to start worrying
132 about remote file systems, we'd have to enable the wrapper function
133 all of the time, just to be safe. That's not worth the cost. */
134 return 0;
135 #elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \
136 - DIR_NAME_SIZE - BUF_SLOP) \
137 <= PATH_MAX)
138 /* FIXME: Assuming there's a system for which this is true,
139 this should be done in a compile test. */
140 return 0;
141 #else
142 /* For a process running under QEMU user-mode, the "/" directory is not
143 really the root directory, but the value of the QEMU_LD_PREFIX environment
144 variable or of the -L command-line option. This causes the logic from
145 glibc/sysdeps/posix/getcwd.c to fail. In this case, skip the test. */
146 if (is_running_under_qemu_user ())
147 return 77;
149 char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1)
150 + DIR_NAME_SIZE + BUF_SLOP];
151 char *cwd = getcwd (buf, PATH_MAX);
152 size_t initial_cwd_len;
153 size_t cwd_len;
154 int fail = 0;
155 size_t n_chdirs = 0;
157 if (cwd == NULL)
158 return 1;
160 cwd_len = initial_cwd_len = strlen (cwd);
162 while (1)
164 # ifdef HAVE_GETCWD_SHORTER
165 /* On OS/X <= 10.9 for example, we're restricted to shorter paths
166 as lstat() doesn't support more than PATH_MAX. */
167 size_t dotdot_max = PATH_MAX * 2;
168 # else
169 size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN);
170 # endif
171 char *c = NULL;
173 cwd_len += DIR_NAME_SIZE;
174 /* If mkdir or chdir fails, it could be that this system cannot create
175 any file with an absolute name longer than PATH_MAX, such as cygwin.
176 If so, leave fail as 0, because the current working directory can't
177 be too long for getcwd if it can't even be created. On Linux with
178 the 9p file system, mkdir fails with error EINVAL when cwd_len gets
179 too long; ignore this failure because the getcwd() system call
180 produces good results whereas the gnulib substitute calls getdents64
181 which fails with error EPROTO.
182 For other errors, be pessimistic and consider that as a failure,
183 too. */
184 if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
186 if (! (errno == ERANGE || errno == ENAMETOOLONG || errno == ENOENT))
187 #ifdef __linux__
188 if (! (errno == EINVAL))
189 #endif
190 fail = 2;
191 break;
194 if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
196 c = getcwd (buf, PATH_MAX);
197 if (!c && errno == ENOENT)
199 fail = 3;
200 break;
202 if (c)
204 fail = 4;
205 break;
207 if (! (errno == ERANGE || errno == ENAMETOOLONG))
209 fail = 5;
210 break;
214 if (dotdot_max <= cwd_len - initial_cwd_len)
216 if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
217 break;
218 c = getcwd (buf, cwd_len + 1);
219 if (!c)
221 if (! (errno == ERANGE || errno == ENOENT
222 || errno == ENAMETOOLONG))
224 fail = 6;
225 break;
227 if (HAVE_OPENAT_SUPPORT || errno == ERANGE || errno == ENOENT)
229 fail = 7;
230 break;
235 if (c && strlen (c) != cwd_len)
237 fail = 8;
238 break;
240 ++n_chdirs;
243 /* Leaving behind such a deep directory is not polite.
244 So clean up here, right away, even though the driving
245 shell script would also clean up. */
247 size_t i;
249 /* Try rmdir first, in case the chdir failed. */
250 rmdir (DIR_NAME);
251 for (i = 0; i <= n_chdirs; i++)
253 if (chdir ("..") < 0)
254 break;
255 if (rmdir (DIR_NAME) != 0)
256 break;
260 return fail;
261 #endif
265 main ()
267 int err1 = test_abort_bug ();
268 int err2 = test_long_name ();
269 int result = err1 * 10 + (err1 != 0 && err2 == 77 ? 0 : err2);
270 return (result ? result : test_exit_status);