getcwd.c revision 1.1.1.1.2.1 1 /* Copyright (C) 1991-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation, either version 3 of the License,
7 or (at your option) any later version.
8
9 This file 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 <https://www.gnu.org/licenses/>. */
16
17 #if !_LIBC
18 # include <config.h>
19 # include <unistd.h>
20 # include "pathmax.h"
21 #else
22 # define HAVE_OPENAT 1
23 # define D_INO_IN_DIRENT 1
24 # define HAVE_MSVC_INVALID_PARAMETER_HANDLER 0
25 # define HAVE_MINIMALLY_WORKING_GETCWD 0
26 #endif
27
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <stdbool.h>
32 #include <stddef.h>
33
34 #include <fcntl.h> /* For AT_FDCWD on Solaris 9. */
35
36 /* If this host provides the openat function or if we're using the
37 gnulib replacement function with a native fdopendir, then enable
38 code below to make getcwd more efficient and robust. */
39 #if defined HAVE_OPENAT || (defined GNULIB_OPENAT && defined HAVE_FDOPENDIR)
40 # define HAVE_OPENAT_SUPPORT 1
41 #else
42 # define HAVE_OPENAT_SUPPORT 0
43 #endif
44
45 #ifndef __set_errno
46 # define __set_errno(val) (errno = (val))
47 #endif
48
49 #include <dirent.h>
50 #ifndef _D_EXACT_NAMLEN
51 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
52 #endif
53 #ifndef _D_ALLOC_NAMLEN
54 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
55 #endif
56
57 #include <unistd.h>
58 #include <stdlib.h>
59 #include <string.h>
60
61 #if _LIBC
62 # ifndef mempcpy
63 # define mempcpy __mempcpy
64 # endif
65 #endif
66
67 #ifndef MAX
68 # define MAX(a, b) ((a) < (b) ? (b) : (a))
69 #endif
70 #ifndef MIN
71 # define MIN(a, b) ((a) < (b) ? (a) : (b))
72 #endif
73
74 /* In this file, PATH_MAX only serves as a threshold for choosing among two
75 algorithms. */
76 #ifndef PATH_MAX
77 # define PATH_MAX 8192
78 #endif
79
80 #if D_INO_IN_DIRENT
81 # define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
82 #else
83 # define MATCHING_INO(dp, ino) true
84 #endif
85
86 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
87 # include "msvc-inval.h"
88 #endif
89
90 #if !_LIBC
91 # define GETCWD_RETURN_TYPE char *
92 # define __close_nocancel_nostatus close
93 # define __getcwd_generic rpl_getcwd
94 # undef stat64
95 # define stat64 stat
96 # define __fstat64 fstat
97 # define __fstatat64 fstatat
98 # define __lstat64 lstat
99 # define __closedir closedir
100 # define __opendir opendir
101 # define __readdir64 readdir
102 # define __fdopendir fdopendir
103 # define __openat openat
104 # define __rewinddir rewinddir
105 # define __openat64 openat
106 # define dirent64 dirent
107 #else
108 # include <not-cancel.h>
109 #endif
110
111 /* The results of opendir() in this file are not used with dirfd and fchdir,
112 and we do not leak fds to any single-threaded code that could use stdio,
113 therefore save some unnecessary recursion in fchdir.c.
114 FIXME - if the kernel ever adds support for multi-thread safety for
115 avoiding standard fds, then we should use opendir_safer and
116 openat_safer. */
117 #ifdef GNULIB_defined_opendir
118 # undef opendir
119 #endif
120 #ifdef GNULIB_defined_closedir
121 # undef closedir
122 #endif
123
124 #if defined _WIN32 && !defined __CYGWIN__
126 # if HAVE_MSVC_INVALID_PARAMETER_HANDLER
127 static char *
128 getcwd_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
153 /* Get the name of the current working directory, and put it in SIZE
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. */
158
159 GETCWD_RETURN_TYPE
160 __getcwd_generic (char *buf, size_t size)
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;
188 struct stat64 st;
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
208 dir = getcwd_system (buf, size);
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];
219 dir = getcwd_system (big_buffer, sizeof big_buffer);
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
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
254 if (__lstat64 (".", &st) < 0)
255 goto lose;
256 thisdev = st.st_dev;
257 thisino = st.st_ino;
258
259 if (__lstat64 ("/", &st) < 0)
260 goto lose;
261 rootdev = st.st_dev;
262 rootino = st.st_ino;
263
264 while (!(thisdev == rootdev && thisino == rootino))
265 {
266 struct dirent64 *d;
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
277 fd = __openat64 (fd, "..", O_RDONLY);
278 if (fd < 0)
279 goto lose;
280 fd_needs_closing = true;
281 parent_status = __fstat64 (fd, &st);
282 #else
283 dotlist[dotlen++] = '.';
284 dotlist[dotlen++] = '.';
285 dotlist[dotlen] = '\0';
286 parent_status = __lstat64 (dotlist, &st);
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
304 dirstream = __fdopendir (fd);
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);
319 d = __readdir64 (dirstream);
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;
331 __rewinddir (dirstream);
332 d = __readdir64 (dirstream);
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
358 entry_status = __fstatat64 (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
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));
395 entry_status = __lstat64 (dotlist, &st);
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. */
463 buf = (used < allocated ? realloc (dir, used) : dir);
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)
481 __close_nocancel_nostatus (fd);
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
493 #if defined _LIBC && !defined GETCWD_RETURN_TYPE
494 libc_hidden_def (__getcwd)
495 weak_alias (__getcwd, getcwd)
496 #endif
497