1 /* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005 Free Software
2 Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: getcwd.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
20
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #if !_LIBC
27 # include "getcwd.h"
28 #endif
29
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35
36 #include <fcntl.h> /* For AT_FDCWD on Solaris 9. */
37
38 #ifndef __set_errno
39 # define __set_errno(val) (errno = (val))
40 #endif
41
42 #if HAVE_DIRENT_H || _LIBC
43 # include <dirent.h>
44 # ifndef _D_EXACT_NAMLEN
45 # define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
46 # endif
47 #else
48 # define dirent direct
49 # if HAVE_SYS_NDIR_H
50 # include <sys/ndir.h>
51 # endif
52 # if HAVE_SYS_DIR_H
53 # include <sys/dir.h>
54 # endif
55 # if HAVE_NDIR_H
56 # include <ndir.h>
57 # endif
58 #endif
59 #ifndef _D_EXACT_NAMLEN
60 # define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
61 #endif
62 #ifndef _D_ALLOC_NAMLEN
63 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
64 #endif
65
66 #if HAVE_UNISTD_H || _LIBC
67 # include <unistd.h>
68 #endif
69
70 #include <stdlib.h>
71 #include <string.h>
72
73 #if _LIBC
74 # ifndef mempcpy
75 # define mempcpy __mempcpy
76 # endif
77 #else
78 # include "mempcpy.h"
79 #endif
80
81 #include <limits.h>
82
83 #ifdef ENAMETOOLONG
84 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
85 #else
86 # define is_ENAMETOOLONG(x) 0
87 #endif
88
89 #ifndef MAX
90 # define MAX(a, b) ((a) < (b) ? (b) : (a))
91 #endif
92 #ifndef MIN
93 # define MIN(a, b) ((a) < (b) ? (a) : (b))
94 #endif
95
96 #ifndef PATH_MAX
97 # ifdef MAXPATHLEN
98 # define PATH_MAX MAXPATHLEN
99 # else
100 # define PATH_MAX 1024
101 # endif
102 #endif
103
104 #if D_INO_IN_DIRENT
105 # define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
106 #else
107 # define MATCHING_INO(dp, ino) true
108 #endif
109
110 #if !_LIBC
111 # define __getcwd getcwd
112 # define __lstat lstat
113 # define __closedir closedir
114 # define __opendir opendir
115 # define __readdir readdir
116 #endif
117
118 /* Get the name of the current working directory, and put it in SIZE
119 bytes of BUF. Returns NULL if the directory couldn't be determined or
120 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
121 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
122 unless SIZE == 0, in which case it is as big as necessary. */
123
124 char *
__getcwd(char * buf,size_t size)125 __getcwd (char *buf, size_t size)
126 {
127 /* Lengths of big file name components and entire file names, and a
128 deep level of file name nesting. These numbers are not upper
129 bounds; they are merely large values suitable for initial
130 allocations, designed to be large enough for most real-world
131 uses. */
132 enum
133 {
134 BIG_FILE_NAME_COMPONENT_LENGTH = 255,
135 BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
136 DEEP_NESTING = 100
137 };
138
139 #ifdef AT_FDCWD
140 int fd = AT_FDCWD;
141 bool fd_needs_closing = false;
142 #else
143 char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
144 char *dotlist = dots;
145 size_t dotsize = sizeof dots;
146 size_t dotlen = 0;
147 #endif
148 DIR *dirstream = NULL;
149 dev_t rootdev, thisdev;
150 ino_t rootino, thisino;
151 char *dir;
152 register char *dirp;
153 struct stat st;
154 size_t allocated = size;
155 size_t used;
156
157 #if HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD
158 /* The system getcwd works, except it sometimes fails when it
159 shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT. If
160 AT_FDCWD is not defined, the algorithm below is O(N**2) and this
161 is much slower than the system getcwd (at least on GNU/Linux).
162 So trust the system getcwd's results unless they look
163 suspicious. */
164 # undef getcwd
165 dir = getcwd (buf, size);
166 if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
167 return dir;
168 #endif
169
170 if (size == 0)
171 {
172 if (buf != NULL)
173 {
174 __set_errno (EINVAL);
175 return NULL;
176 }
177
178 allocated = BIG_FILE_NAME_LENGTH + 1;
179 }
180
181 if (buf == NULL)
182 {
183 dir = malloc (allocated);
184 if (dir == NULL)
185 return NULL;
186 }
187 else
188 dir = buf;
189
190 dirp = dir + allocated;
191 *--dirp = '\0';
192
193 if (__lstat (".", &st) < 0)
194 goto lose;
195 thisdev = st.st_dev;
196 thisino = st.st_ino;
197
198 if (__lstat ("/", &st) < 0)
199 goto lose;
200 rootdev = st.st_dev;
201 rootino = st.st_ino;
202
203 while (!(thisdev == rootdev && thisino == rootino))
204 {
205 struct dirent *d;
206 dev_t dotdev;
207 ino_t dotino;
208 bool mount_point;
209 int parent_status;
210
211 /* Look at the parent directory. */
212 #ifdef AT_FDCWD
213 fd = openat (fd, "..", O_RDONLY);
214 if (fd < 0)
215 goto lose;
216 fd_needs_closing = true;
217 parent_status = fstat (fd, &st);
218 #else
219 dotlist[dotlen++] = '.';
220 dotlist[dotlen++] = '.';
221 dotlist[dotlen] = '\0';
222 parent_status = __lstat (dotlist, &st);
223 #endif
224 if (parent_status != 0)
225 goto lose;
226
227 if (dirstream && __closedir (dirstream) != 0)
228 {
229 dirstream = NULL;
230 goto lose;
231 }
232
233 /* Figure out if this directory is a mount point. */
234 dotdev = st.st_dev;
235 dotino = st.st_ino;
236 mount_point = dotdev != thisdev;
237
238 /* Search for the last directory. */
239 #ifdef AT_FDCWD
240 dirstream = fdopendir (fd);
241 if (dirstream == NULL)
242 goto lose;
243 fd_needs_closing = false;
244 #else
245 dirstream = __opendir (dotlist);
246 if (dirstream == NULL)
247 goto lose;
248 dotlist[dotlen++] = '/';
249 #endif
250 /* Clear errno to distinguish EOF from error if readdir returns
251 NULL. */
252 __set_errno (0);
253 while ((d = __readdir (dirstream)) != NULL)
254 {
255 if (d->d_name[0] == '.' &&
256 (d->d_name[1] == '\0' ||
257 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
258 continue;
259 if (MATCHING_INO (d, thisino) || mount_point)
260 {
261 int entry_status;
262 #ifdef AT_FDCWD
263 entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
264 #else
265 /* Compute size needed for this file name, or for the file
266 name ".." in the same directory, whichever is larger.
267 Room for ".." might be needed the next time through
268 the outer loop. */
269 size_t name_alloc = _D_ALLOC_NAMLEN (d);
270 size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
271
272 if (filesize < dotlen)
273 goto memory_exhausted;
274
275 if (dotsize < filesize)
276 {
277 /* My, what a deep directory tree you have, Grandma. */
278 size_t newsize = MAX (filesize, dotsize * 2);
279 size_t i;
280 if (newsize < dotsize)
281 goto memory_exhausted;
282 if (dotlist != dots)
283 free (dotlist);
284 dotlist = malloc (newsize);
285 if (dotlist == NULL)
286 goto lose;
287 dotsize = newsize;
288
289 i = 0;
290 do
291 {
292 dotlist[i++] = '.';
293 dotlist[i++] = '.';
294 dotlist[i++] = '/';
295 }
296 while (i < dotlen);
297 }
298
299 strcpy (dotlist + dotlen, d->d_name);
300 entry_status = __lstat (dotlist, &st);
301 #endif
302 /* We don't fail here if we cannot stat() a directory entry.
303 This can happen when (network) file systems fail. If this
304 entry is in fact the one we are looking for we will find
305 out soon as we reach the end of the directory without
306 having found anything. */
307 if (entry_status == 0 && S_ISDIR (st.st_mode)
308 && st.st_dev == thisdev && st.st_ino == thisino)
309 break;
310 }
311 }
312 if (d == NULL)
313 {
314 if (errno == 0)
315 /* EOF on dirstream, which means that the current directory
316 has been removed. */
317 __set_errno (ENOENT);
318 goto lose;
319 }
320 else
321 {
322 size_t dirroom = dirp - dir;
323 size_t namlen = _D_EXACT_NAMLEN (d);
324
325 if (dirroom <= namlen)
326 {
327 if (size != 0)
328 {
329 __set_errno (ERANGE);
330 goto lose;
331 }
332 else
333 {
334 char *tmp;
335 size_t oldsize = allocated;
336
337 allocated += MAX (allocated, namlen);
338 if (allocated < oldsize
339 || ! (tmp = realloc (dir, allocated)))
340 goto memory_exhausted;
341
342 /* Move current contents up to the end of the buffer.
343 This is guaranteed to be non-overlapping. */
344 dirp = memcpy (tmp + allocated - (oldsize - dirroom),
345 tmp + dirroom,
346 oldsize - dirroom);
347 dir = tmp;
348 }
349 }
350 dirp -= namlen;
351 memcpy (dirp, d->d_name, namlen);
352 *--dirp = '/';
353 }
354
355 thisdev = dotdev;
356 thisino = dotino;
357 }
358
359 if (dirstream && __closedir (dirstream) != 0)
360 {
361 dirstream = NULL;
362 goto lose;
363 }
364
365 if (dirp == &dir[allocated - 1])
366 *--dirp = '/';
367
368 #ifndef AT_FDCWD
369 if (dotlist != dots)
370 free (dotlist);
371 #endif
372
373 used = dir + allocated - dirp;
374 memmove (dir, dirp, used);
375
376 if (buf == NULL && size == 0)
377 /* Ensure that the buffer is only as large as necessary. */
378 buf = realloc (dir, used);
379
380 if (buf == NULL)
381 /* Either buf was NULL all along, or `realloc' failed but
382 we still have the original string. */
383 buf = dir;
384
385 return buf;
386
387 memory_exhausted:
388 __set_errno (ENOMEM);
389 lose:
390 {
391 int save = errno;
392 if (dirstream)
393 __closedir (dirstream);
394 #ifdef AT_FDCWD
395 if (fd_needs_closing)
396 close (fd);
397 #else
398 if (dotlist != dots)
399 free (dotlist);
400 #endif
401 if (buf == NULL)
402 free (dir);
403 __set_errno (save);
404 }
405 return NULL;
406 }
407
408 #ifdef weak_alias
409 weak_alias (__getcwd, getcwd)
410 #endif
411