gnulib: update to 776af40e0
[deliverable/binutils-gdb.git] / gnulib / import / getcwd.c
CommitLineData
9c9d63b1 1/* Copyright (C) 1991-2021 Free Software Foundation, Inc.
6ec2e0f5
SDJ
2 This file is part of the GNU C Library.
3
9c9d63b1
PM
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
6ec2e0f5 8
9c9d63b1 9 The GNU C Library is distributed in the hope that it will be useful,
6ec2e0f5 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
9c9d63b1
PM
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
6ec2e0f5 13
9c9d63b1
PM
14 You should have received a copy of the GNU General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
6ec2e0f5
SDJ
17
18#if !_LIBC
19# include <config.h>
20# include <unistd.h>
9c9d63b1
PM
21# include "pathmax.h"
22#else
23# define HAVE_OPENAT 1
24# define D_INO_IN_DIRENT 1
25# define HAVE_MSVC_INVALID_PARAMETER_HANDLER 0
26# define HAVE_MINIMALLY_WORKING_GETCWD 0
6ec2e0f5
SDJ
27#endif
28
29#include <errno.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <stdbool.h>
33#include <stddef.h>
34
35#include <fcntl.h> /* For AT_FDCWD on Solaris 9. */
36
37/* If this host provides the openat function or if we're using the
38 gnulib replacement function with a native fdopendir, then enable
39 code below to make getcwd more efficient and robust. */
40#if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
41# define HAVE_OPENAT_SUPPORT 1
42#else
43# define HAVE_OPENAT_SUPPORT 0
44#endif
45
46#ifndef __set_errno
47# define __set_errno(val) (errno = (val))
48#endif
49
50#include <dirent.h>
51#ifndef _D_EXACT_NAMLEN
52# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
53#endif
54#ifndef _D_ALLOC_NAMLEN
55# define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
56#endif
57
58#include <unistd.h>
59#include <stdlib.h>
60#include <string.h>
61
62#if _LIBC
63# ifndef mempcpy
64# define mempcpy __mempcpy
65# endif
66#endif
67
68#ifndef MAX
69# define MAX(a, b) ((a) < (b) ? (b) : (a))
70#endif
71#ifndef MIN
72# define MIN(a, b) ((a) < (b) ? (a) : (b))
73#endif
74
6ec2e0f5
SDJ
75/* In this file, PATH_MAX only serves as a threshold for choosing among two
76 algorithms. */
77#ifndef PATH_MAX
78# define PATH_MAX 8192
79#endif
80
81#if D_INO_IN_DIRENT
82# define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
83#else
84# define MATCHING_INO(dp, ino) true
85#endif
86
c0c3707f
CB
87#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
88# include "msvc-inval.h"
89#endif
90
6ec2e0f5 91#if !_LIBC
9c9d63b1
PM
92# define GETCWD_RETURN_TYPE char *
93# define __close_nocancel_nostatus close
94# define __getcwd_generic rpl_getcwd
95# undef stat64
96# define stat64 stat
97# define __fstat64 fstat
98# define __fstatat64 fstatat
99# define __lstat64 lstat
6ec2e0f5
SDJ
100# define __closedir closedir
101# define __opendir opendir
9c9d63b1
PM
102# define __readdir64 readdir
103# define __fdopendir fdopendir
104# define __openat openat
105# define __rewinddir rewinddir
106# define __openat64 openat
107# define dirent64 dirent
108#else
109# include <not-cancel.h>
6ec2e0f5
SDJ
110#endif
111
112/* The results of opendir() in this file are not used with dirfd and fchdir,
113 and we do not leak fds to any single-threaded code that could use stdio,
114 therefore save some unnecessary recursion in fchdir.c.
115 FIXME - if the kernel ever adds support for multi-thread safety for
116 avoiding standard fds, then we should use opendir_safer and
117 openat_safer. */
118#ifdef GNULIB_defined_opendir
119# undef opendir
120#endif
121#ifdef GNULIB_defined_closedir
122# undef closedir
123#endif
124\f
9c9d63b1 125#if defined _WIN32 && !defined __CYGWIN__
c0c3707f
CB
126# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
127static char *
128getcwd_nothrow (char *buf, size_t size)
129{
130 char *result;
131
132 TRY_MSVC_INVAL
133 {
134 result = _getcwd (buf, size);
135 }
136 CATCH_MSVC_INVAL
137 {
138 result = NULL;
139 errno = ERANGE;
140 }
141 DONE_MSVC_INVAL;
142
143 return result;
144}
145# else
146# define getcwd_nothrow _getcwd
147# endif
148# define getcwd_system getcwd_nothrow
149#else
150# define getcwd_system getcwd
151#endif
152
6ec2e0f5 153/* Get the name of the current working directory, and put it in SIZE
698be2d8
CB
154 bytes of BUF. Returns NULL with errno set if the directory couldn't be
155 determined or SIZE was too small. If successful, returns BUF. In GNU,
156 if BUF is NULL, an array is allocated with 'malloc'; the array is SIZE
157 bytes long, unless SIZE == 0, in which case it is as big as necessary. */
6ec2e0f5 158
9c9d63b1
PM
159GETCWD_RETURN_TYPE
160__getcwd_generic (char *buf, size_t size)
6ec2e0f5
SDJ
161{
162 /* Lengths of big file name components and entire file names, and a
163 deep level of file name nesting. These numbers are not upper
164 bounds; they are merely large values suitable for initial
165 allocations, designed to be large enough for most real-world
166 uses. */
167 enum
168 {
169 BIG_FILE_NAME_COMPONENT_LENGTH = 255,
170 BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
171 DEEP_NESTING = 100
172 };
173
174#if HAVE_OPENAT_SUPPORT
175 int fd = AT_FDCWD;
176 bool fd_needs_closing = false;
177#else
178 char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
179 char *dotlist = dots;
180 size_t dotsize = sizeof dots;
181 size_t dotlen = 0;
182#endif
183 DIR *dirstream = NULL;
184 dev_t rootdev, thisdev;
185 ino_t rootino, thisino;
186 char *dir;
187 register char *dirp;
9c9d63b1 188 struct stat64 st;
6ec2e0f5
SDJ
189 size_t allocated = size;
190 size_t used;
191
192#if HAVE_MINIMALLY_WORKING_GETCWD
193 /* If AT_FDCWD is not defined, the algorithm below is O(N**2) and
194 this is much slower than the system getcwd (at least on
195 GNU/Linux). So trust the system getcwd's results unless they
196 look suspicious.
197
198 Use the system getcwd even if we have openat support, since the
199 system getcwd works even when a parent is unreadable, while the
200 openat-based approach does not.
201
202 But on AIX 5.1..7.1, the system getcwd is not even minimally
203 working: If the current directory name is slightly longer than
204 PATH_MAX, it omits the first directory component and returns
205 this wrong result with errno = 0. */
206
207# undef getcwd
c0c3707f 208 dir = getcwd_system (buf, size);
6ec2e0f5
SDJ
209 if (dir || (size && errno == ERANGE))
210 return dir;
211
212 /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
213 internal magic that lets it work even if an ancestor directory is
214 inaccessible, which is better in many cases. So in this case try
215 again with a buffer that's almost always big enough. */
216 if (errno == EINVAL && buf == NULL && size == 0)
217 {
218 char big_buffer[BIG_FILE_NAME_LENGTH + 1];
c0c3707f 219 dir = getcwd_system (big_buffer, sizeof big_buffer);
6ec2e0f5
SDJ
220 if (dir)
221 return strdup (dir);
222 }
223
224# if HAVE_PARTLY_WORKING_GETCWD
225 /* The system getcwd works, except it sometimes fails when it
226 shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. */
227 if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
228 return NULL;
229# endif
230#endif
6ec2e0f5
SDJ
231 if (size == 0)
232 {
233 if (buf != NULL)
234 {
235 __set_errno (EINVAL);
236 return NULL;
237 }
238
239 allocated = BIG_FILE_NAME_LENGTH + 1;
240 }
241
242 if (buf == NULL)
243 {
244 dir = malloc (allocated);
245 if (dir == NULL)
246 return NULL;
247 }
248 else
249 dir = buf;
250
251 dirp = dir + allocated;
252 *--dirp = '\0';
253
9c9d63b1 254 if (__lstat64 (".", &st) < 0)
6ec2e0f5
SDJ
255 goto lose;
256 thisdev = st.st_dev;
257 thisino = st.st_ino;
258
9c9d63b1 259 if (__lstat64 ("/", &st) < 0)
6ec2e0f5
SDJ
260 goto lose;
261 rootdev = st.st_dev;
262 rootino = st.st_ino;
263
264 while (!(thisdev == rootdev && thisino == rootino))
265 {
9c9d63b1 266 struct dirent64 *d;
6ec2e0f5
SDJ
267 dev_t dotdev;
268 ino_t dotino;
269 bool mount_point;
270 int parent_status;
271 size_t dirroom;
272 size_t namlen;
273 bool use_d_ino = true;
274
275 /* Look at the parent directory. */
276#if HAVE_OPENAT_SUPPORT
9c9d63b1 277 fd = __openat64 (fd, "..", O_RDONLY);
6ec2e0f5
SDJ
278 if (fd < 0)
279 goto lose;
280 fd_needs_closing = true;
9c9d63b1 281 parent_status = __fstat64 (fd, &st);
6ec2e0f5
SDJ
282#else
283 dotlist[dotlen++] = '.';
284 dotlist[dotlen++] = '.';
285 dotlist[dotlen] = '\0';
9c9d63b1 286 parent_status = __lstat64 (dotlist, &st);
6ec2e0f5
SDJ
287#endif
288 if (parent_status != 0)
289 goto lose;
290
291 if (dirstream && __closedir (dirstream) != 0)
292 {
293 dirstream = NULL;
294 goto lose;
295 }
296
297 /* Figure out if this directory is a mount point. */
298 dotdev = st.st_dev;
299 dotino = st.st_ino;
300 mount_point = dotdev != thisdev;
301
302 /* Search for the last directory. */
303#if HAVE_OPENAT_SUPPORT
9c9d63b1 304 dirstream = __fdopendir (fd);
6ec2e0f5
SDJ
305 if (dirstream == NULL)
306 goto lose;
307 fd_needs_closing = false;
308#else
309 dirstream = __opendir (dotlist);
310 if (dirstream == NULL)
311 goto lose;
312 dotlist[dotlen++] = '/';
313#endif
314 for (;;)
315 {
316 /* Clear errno to distinguish EOF from error if readdir returns
317 NULL. */
318 __set_errno (0);
9c9d63b1 319 d = __readdir64 (dirstream);
6ec2e0f5
SDJ
320
321 /* When we've iterated through all directory entries without finding
322 one with a matching d_ino, rewind the stream and consider each
323 name again, but this time, using lstat. This is necessary in a
324 chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
325 .., ../.., ../../.., etc. all had the same device number, yet the
326 d_ino values for entries in / did not match those obtained
327 via lstat. */
328 if (d == NULL && errno == 0 && use_d_ino)
329 {
330 use_d_ino = false;
9c9d63b1
PM
331 __rewinddir (dirstream);
332 d = __readdir64 (dirstream);
6ec2e0f5
SDJ
333 }
334
335 if (d == NULL)
336 {
337 if (errno == 0)
338 /* EOF on dirstream, which can mean e.g., that the current
339 directory has been removed. */
340 __set_errno (ENOENT);
341 goto lose;
342 }
343 if (d->d_name[0] == '.' &&
344 (d->d_name[1] == '\0' ||
345 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
346 continue;
347
348 if (use_d_ino)
349 {
350 bool match = (MATCHING_INO (d, thisino) || mount_point);
351 if (! match)
352 continue;
353 }
354
355 {
356 int entry_status;
357#if HAVE_OPENAT_SUPPORT
9c9d63b1 358 entry_status = __fstatat64 (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
6ec2e0f5
SDJ
359#else
360 /* Compute size needed for this file name, or for the file
361 name ".." in the same directory, whichever is larger.
362 Room for ".." might be needed the next time through
363 the outer loop. */
364 size_t name_alloc = _D_ALLOC_NAMLEN (d);
365 size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
366
367 if (filesize < dotlen)
368 goto memory_exhausted;
369
370 if (dotsize < filesize)
371 {
372 /* My, what a deep directory tree you have, Grandma. */
373 size_t newsize = MAX (filesize, dotsize * 2);
374 size_t i;
375 if (newsize < dotsize)
376 goto memory_exhausted;
377 if (dotlist != dots)
378 free (dotlist);
379 dotlist = malloc (newsize);
380 if (dotlist == NULL)
381 goto lose;
382 dotsize = newsize;
383
384 i = 0;
385 do
386 {
387 dotlist[i++] = '.';
388 dotlist[i++] = '.';
389 dotlist[i++] = '/';
390 }
391 while (i < dotlen);
392 }
393
394 memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
9c9d63b1 395 entry_status = __lstat64 (dotlist, &st);
6ec2e0f5
SDJ
396#endif
397 /* We don't fail here if we cannot stat() a directory entry.
398 This can happen when (network) file systems fail. If this
399 entry is in fact the one we are looking for we will find
400 out soon as we reach the end of the directory without
401 having found anything. */
402 if (entry_status == 0 && S_ISDIR (st.st_mode)
403 && st.st_dev == thisdev && st.st_ino == thisino)
404 break;
405 }
406 }
407
408 dirroom = dirp - dir;
409 namlen = _D_EXACT_NAMLEN (d);
410
411 if (dirroom <= namlen)
412 {
413 if (size != 0)
414 {
415 __set_errno (ERANGE);
416 goto lose;
417 }
418 else
419 {
420 char *tmp;
421 size_t oldsize = allocated;
422
423 allocated += MAX (allocated, namlen);
424 if (allocated < oldsize
425 || ! (tmp = realloc (dir, allocated)))
426 goto memory_exhausted;
427
428 /* Move current contents up to the end of the buffer.
429 This is guaranteed to be non-overlapping. */
430 dirp = memcpy (tmp + allocated - (oldsize - dirroom),
431 tmp + dirroom,
432 oldsize - dirroom);
433 dir = tmp;
434 }
435 }
436 dirp -= namlen;
437 memcpy (dirp, d->d_name, namlen);
438 *--dirp = '/';
439
440 thisdev = dotdev;
441 thisino = dotino;
442 }
443
444 if (dirstream && __closedir (dirstream) != 0)
445 {
446 dirstream = NULL;
447 goto lose;
448 }
449
450 if (dirp == &dir[allocated - 1])
451 *--dirp = '/';
452
453#if ! HAVE_OPENAT_SUPPORT
454 if (dotlist != dots)
455 free (dotlist);
456#endif
457
458 used = dir + allocated - dirp;
459 memmove (dir, dirp, used);
460
461 if (size == 0)
462 /* Ensure that the buffer is only as large as necessary. */
c0c3707f 463 buf = (used < allocated ? realloc (dir, used) : dir);
6ec2e0f5
SDJ
464
465 if (buf == NULL)
466 /* Either buf was NULL all along, or 'realloc' failed but
467 we still have the original string. */
468 buf = dir;
469
470 return buf;
471
472 memory_exhausted:
473 __set_errno (ENOMEM);
474 lose:
475 {
476 int save = errno;
477 if (dirstream)
478 __closedir (dirstream);
479#if HAVE_OPENAT_SUPPORT
480 if (fd_needs_closing)
9c9d63b1 481 __close_nocancel_nostatus (fd);
6ec2e0f5
SDJ
482#else
483 if (dotlist != dots)
484 free (dotlist);
485#endif
486 if (buf == NULL)
487 free (dir);
488 __set_errno (save);
489 }
490 return NULL;
491}
492
9c9d63b1
PM
493#if defined _LIBC && !defined GETCWD_RETURN_TYPE
494libc_hidden_def (__getcwd)
6ec2e0f5
SDJ
495weak_alias (__getcwd, getcwd)
496#endif
This page took 0.268631 seconds and 4 git commands to generate.