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