getcwd.c revision 150172
1/*
2 * Copyright (c) 1989, 1991, 1993, 1995
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#if defined(LIBC_SCCS) && !defined(lint)
35static char sccsid[] = "@(#)getcwd.c	8.5 (Berkeley) 2/7/95";
36#endif /* LIBC_SCCS and not lint */
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/lib/libc/gen/getcwd.c 150172 2005-09-15 17:25:52Z ache $");
39
40#include "namespace.h"
41#include <sys/param.h>
42#include <sys/stat.h>
43
44#include <dirent.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51#include "un-namespace.h"
52
53#define	ISDOT(dp) \
54	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
55	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
56
57extern int __getcwd(char *, size_t);
58
59char *
60getcwd(pt, size)
61	char *pt;
62	size_t size;
63{
64	struct dirent *dp;
65	DIR *dir = NULL;
66	dev_t dev;
67	ino_t ino;
68	int first;
69	char *bpt, *bup;
70	struct stat s;
71	dev_t root_dev;
72	ino_t root_ino;
73	size_t ptsize, upsize;
74	int save_errno;
75	char *ept, *eup, *up, c;
76
77	/*
78	 * If no buffer specified by the user, allocate one as necessary.
79	 * If a buffer is specified, the size has to be non-zero.  The path
80	 * is built from the end of the buffer backwards.
81	 */
82	if (pt) {
83		ptsize = 0;
84		if (!size) {
85			errno = EINVAL;
86			return (NULL);
87		}
88		if (size == 1) {
89			errno = ERANGE;
90			return (NULL);
91		}
92		ept = pt + size;
93	} else {
94		if ((pt = malloc(ptsize = PATH_MAX)) == NULL)
95			return (NULL);
96		ept = pt + ptsize;
97	}
98	if (__getcwd(pt, ept - pt) == 0) {
99		if (*pt != '/') {
100			bpt = pt;
101			ept = pt + strlen(pt) - 1;
102			while (bpt < ept) {
103				c = *bpt;
104				*bpt++ = *ept;
105				*ept-- = c;
106			}
107		}
108		return (pt);
109	}
110	bpt = ept - 1;
111	*bpt = '\0';
112
113	/*
114	 * Allocate 1024 bytes for the string of "../"'s.
115	 * Should always be enough.  If it's not, allocate
116	 * as necessary.  Special case the first stat, it's ".", not "..".
117	 */
118	if ((up = malloc(upsize = 1024)) == NULL)
119		goto err;
120	eup = up + upsize;
121	bup = up;
122	up[0] = '.';
123	up[1] = '\0';
124
125	/* Save root values, so know when to stop. */
126	if (stat("/", &s))
127		goto err;
128	root_dev = s.st_dev;
129	root_ino = s.st_ino;
130
131	errno = 0;			/* XXX readdir has no error return. */
132
133	for (first = 1;; first = 0) {
134		/* Stat the current level. */
135		if (lstat(up, &s))
136			goto err;
137
138		/* Save current node values. */
139		ino = s.st_ino;
140		dev = s.st_dev;
141
142		/* Check for reaching root. */
143		if (root_dev == dev && root_ino == ino) {
144			*--bpt = '/';
145			/*
146			 * It's unclear that it's a requirement to copy the
147			 * path to the beginning of the buffer, but it's always
148			 * been that way and stuff would probably break.
149			 */
150			bcopy(bpt, pt, ept - bpt);
151			free(up);
152			return (pt);
153		}
154
155		/*
156		 * Build pointer to the parent directory, allocating memory
157		 * as necessary.  Max length is 3 for "../", the largest
158		 * possible component name, plus a trailing NUL.
159		 */
160		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
161			if ((up = reallocf(up, upsize *= 2)) == NULL)
162				goto err;
163			bup = up;
164			eup = up + upsize;
165		}
166		*bup++ = '.';
167		*bup++ = '.';
168		*bup = '\0';
169
170		/* Open and stat parent directory. */
171		if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s))
172			goto err;
173
174		/* Add trailing slash for next directory. */
175		*bup++ = '/';
176		*bup = '\0';
177
178		/*
179		 * If it's a mount point, have to stat each element because
180		 * the inode number in the directory is for the entry in the
181		 * parent directory, not the inode number of the mounted file.
182		 */
183		save_errno = 0;
184		if (s.st_dev == dev) {
185			for (;;) {
186				if (!(dp = readdir(dir)))
187					goto notfound;
188				if (dp->d_fileno == ino)
189					break;
190			}
191		} else
192			for (;;) {
193				if (!(dp = readdir(dir)))
194					goto notfound;
195				if (ISDOT(dp))
196					continue;
197				bcopy(dp->d_name, bup, dp->d_namlen + 1);
198
199				/* Save the first error for later. */
200				if (lstat(up, &s)) {
201					if (!save_errno)
202						save_errno = errno;
203					errno = 0;
204					continue;
205				}
206				if (s.st_dev == dev && s.st_ino == ino)
207					break;
208			}
209
210		/*
211		 * Check for length of the current name, preceding slash,
212		 * leading slash.
213		 */
214		if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
215			size_t len, off;
216
217			if (!ptsize) {
218				errno = ERANGE;
219				goto err;
220			}
221			off = bpt - pt;
222			len = ept - bpt;
223			if ((pt = reallocf(pt, ptsize *= 2)) == NULL)
224				goto err;
225			bpt = pt + off;
226			ept = pt + ptsize;
227			bcopy(bpt, ept - len, len);
228			bpt = ept - len;
229		}
230		if (!first)
231			*--bpt = '/';
232		bpt -= dp->d_namlen;
233		bcopy(dp->d_name, bpt, dp->d_namlen);
234		(void) closedir(dir);
235		dir = NULL;
236
237		/* Truncate any file name. */
238		*bup = '\0';
239	}
240
241notfound:
242	/*
243	 * If readdir set errno, use it, not any saved error; otherwise,
244	 * didn't find the current directory in its parent directory, set
245	 * errno to ENOENT.
246	 */
247	if (!errno)
248		errno = save_errno ? save_errno : ENOENT;
249	/* FALLTHROUGH */
250err:
251	save_errno = errno;
252
253	if (ptsize)
254		free(pt);
255	if (dir)
256		(void) closedir(dir);
257	free(up);
258
259	errno = save_errno;
260	return (NULL);
261}
262