getcwd.c revision 71579
155714Skris/*
255714Skris * Copyright (c) 1989, 1991, 1993, 1995
355714Skris *	The Regents of the University of California.  All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8296465Sdelphij * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris * 3. All advertising materials mentioning features or use of this software
1455714Skris *    must display the following acknowledgement:
15296465Sdelphij *	This product includes software developed by the University of
1655714Skris *	California, Berkeley and its contributors.
1755714Skris * 4. Neither the name of the University nor the names of its contributors
1855714Skris *    may be used to endorse or promote products derived from this software
1955714Skris *    without specific prior written permission.
2055714Skris *
2155714Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22296465Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155714Skris * SUCH DAMAGE.
3255714Skris *
3355714Skris * $FreeBSD: head/lib/libc/gen/getcwd.c 71579 2001-01-24 13:01:12Z deischen $
3455714Skris */
3555714Skris
3655714Skris#if defined(LIBC_SCCS) && !defined(lint)
37296465Sdelphijstatic char sccsid[] = "@(#)getcwd.c	8.5 (Berkeley) 2/7/95";
3855714Skris#endif /* LIBC_SCCS and not lint */
3955714Skris
40296465Sdelphij#include "namespace.h"
4155714Skris#include <sys/param.h>
4255714Skris#include <sys/stat.h>
4355714Skris
4455714Skris#include <dirent.h>
4555714Skris#include <errno.h>
4655714Skris#include <fcntl.h>
4755714Skris#include <stdio.h>
4855714Skris#include <stdlib.h>
4955714Skris#include <string.h>
5055714Skris#include <unistd.h>
5155714Skris#include "un-namespace.h"
52296465Sdelphij
5355714Skris#define	ISDOT(dp) \
5455714Skris	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
5555714Skris	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
5655714Skris
5755714Skrischar *
5855714Skrisgetcwd(pt, size)
5955714Skris	char *pt;
60296465Sdelphij	size_t size;
6155714Skris{
6255714Skris	register struct dirent *dp;
6355714Skris	register DIR *dir = NULL;
6455714Skris	register dev_t dev;
6555714Skris	register ino_t ino;
66296465Sdelphij	register int first;
67296465Sdelphij	register char *bpt, *bup;
6855714Skris	struct stat s;
69296465Sdelphij	dev_t root_dev;
70296465Sdelphij	ino_t root_ino;
71296465Sdelphij	size_t ptsize, upsize;
72296465Sdelphij	int save_errno;
7355714Skris	char *ept, *eup, *up, c;
7455714Skris
75296465Sdelphij	/*
7655714Skris	 * If no buffer specified by the user, allocate one as necessary.
7755714Skris	 * If a buffer is specified, the size has to be non-zero.  The path
7855714Skris	 * is built from the end of the buffer backwards.
7955714Skris	 */
8055714Skris	if (pt) {
8155714Skris		ptsize = 0;
8255714Skris		if (!size) {
8355714Skris			errno = EINVAL;
8455714Skris			return (NULL);
8555714Skris		}
8655714Skris		if (size == 1) {
8755714Skris			errno = ERANGE;
8855714Skris			return (NULL);
8955714Skris		}
9055714Skris		ept = pt + size;
9155714Skris	} else {
9255714Skris		if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
9355714Skris			return (NULL);
9455714Skris		ept = pt + ptsize;
9555714Skris	}
9655714Skris#if	!defined(__NETBSD_SYSCALLS)
9755714Skris	if (__getcwd(pt, ept - pt) == 0) {
9855714Skris		if (*pt != '/') {
9955714Skris			bpt = pt;
10055714Skris			ept = pt + strlen(pt) - 1;
10155714Skris			while (bpt < ept) {
10255714Skris				c = *bpt;
10355714Skris				*bpt++ = *ept;
10455714Skris				*ept-- = c;
10555714Skris			}
106296465Sdelphij		}
10755714Skris		return (pt);
10855714Skris	}
10955714Skris#endif
11055714Skris	bpt = ept - 1;
11155714Skris	*bpt = '\0';
11255714Skris
11355714Skris	/*
11455714Skris	 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
11555714Skris	 * Should always be enough (it's 340 levels).  If it's not, allocate
11655714Skris	 * as necessary.  Special case the first stat, it's ".", not "..".
11755714Skris	 */
11855714Skris	if ((up = malloc(upsize = 1024 - 4)) == NULL)
119296465Sdelphij		goto err;
120296465Sdelphij	eup = up + MAXPATHLEN;
12155714Skris	bup = up;
12255714Skris	up[0] = '.';
12355714Skris	up[1] = '\0';
124296465Sdelphij
12555714Skris	/* Save root values, so know when to stop. */
12655714Skris	if (stat("/", &s))
127109998Smarkm		goto err;
12855714Skris	root_dev = s.st_dev;
12955714Skris	root_ino = s.st_ino;
13055714Skris
13155714Skris	errno = 0;			/* XXX readdir has no error return. */
13255714Skris
13355714Skris	for (first = 1;; first = 0) {
13455714Skris		/* Stat the current level. */
135296465Sdelphij		if (lstat(up, &s))
136296465Sdelphij			goto err;
137296465Sdelphij
13855714Skris		/* Save current node values. */
13955714Skris		ino = s.st_ino;
140296465Sdelphij		dev = s.st_dev;
141296465Sdelphij
14255714Skris		/* Check for reaching root. */
143296465Sdelphij		if (root_dev == dev && root_ino == ino) {
14455714Skris			*--bpt = '/';
14555714Skris			/*
14655714Skris			 * It's unclear that it's a requirement to copy the
147296465Sdelphij			 * path to the beginning of the buffer, but it's always
148296465Sdelphij			 * been that way and stuff would probably break.
149296465Sdelphij			 */
150296465Sdelphij			bcopy(bpt, pt, ept - bpt);
151296465Sdelphij			free(up);
152296465Sdelphij			return (pt);
153296465Sdelphij		}
154296465Sdelphij
155296465Sdelphij		/*
156296465Sdelphij		 * Build pointer to the parent directory, allocating memory
15755714Skris		 * as necessary.  Max length is 3 for "../", the largest
15855714Skris		 * possible component name, plus a trailing NULL.
15955714Skris		 */
16055714Skris		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
16155714Skris			if ((up = reallocf(up, upsize *= 2)) == NULL)
16255714Skris				goto err;
16355714Skris			bup = up;
16455714Skris			eup = up + upsize;
16555714Skris		}
16655714Skris		*bup++ = '.';
16755714Skris		*bup++ = '.';
168296465Sdelphij		*bup = '\0';
16955714Skris
170296465Sdelphij		/* Open and stat parent directory. */
171296465Sdelphij		if (!(dir = opendir(up)) || _fstat(dirfd(dir), &s))
17255714Skris			goto err;
173296465Sdelphij
17455714Skris		/* Add trailing slash for next directory. */
17555714Skris		*bup++ = '/';
17655714Skris		*bup = '\0';
177296465Sdelphij
178296465Sdelphij		/*
179296465Sdelphij		 * If it's a mount point, have to stat each element because
180296465Sdelphij		 * the inode number in the directory is for the entry in the
181296465Sdelphij		 * parent directory, not the inode number of the mounted file.
182296465Sdelphij		 */
18355714Skris		save_errno = 0;
18455714Skris		if (s.st_dev == dev) {
18555714Skris			for (;;) {
186296465Sdelphij				if (!(dp = readdir(dir)))
18755714Skris					goto notfound;
18855714Skris				if (dp->d_fileno == ino)
18955714Skris					break;
190296465Sdelphij			}
191296465Sdelphij		} else
192296465Sdelphij			for (;;) {
193296465Sdelphij				if (!(dp = readdir(dir)))
19455714Skris					goto notfound;
19555714Skris				if (ISDOT(dp))
196296465Sdelphij					continue;
19755714Skris				bcopy(dp->d_name, bup, dp->d_namlen + 1);
198296465Sdelphij
199296465Sdelphij				/* Save the first error for later. */
20055714Skris				if (lstat(up, &s)) {
201296465Sdelphij					if (!save_errno)
20255714Skris						save_errno = errno;
20355714Skris					errno = 0;
20455714Skris					continue;
205296465Sdelphij				}
20655714Skris				if (s.st_dev == dev && s.st_ino == ino)
207296465Sdelphij					break;
20855714Skris			}
20955714Skris
21055714Skris		/*
21155714Skris		 * Check for length of the current name, preceding slash,
21255714Skris		 * leading slash.
213296465Sdelphij		 */
214296465Sdelphij		if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
215296465Sdelphij			size_t len, off;
216296465Sdelphij
217296465Sdelphij			if (!ptsize) {
218296465Sdelphij				errno = ERANGE;
21955714Skris				goto err;
22055714Skris			}
22155714Skris			off = bpt - pt;
22255714Skris			len = ept - bpt;
223296465Sdelphij			if ((pt = realloc(pt, ptsize *= 2)) == NULL)
224296465Sdelphij				goto err;
22555714Skris			bpt = pt + off;
22655714Skris			ept = pt + ptsize;
22755714Skris			bcopy(bpt, ept - len, len);
228296465Sdelphij			bpt = ept - len;
229296465Sdelphij		}
230296465Sdelphij		if (!first)
231296465Sdelphij			*--bpt = '/';
232296465Sdelphij		bpt -= dp->d_namlen;
233296465Sdelphij		bcopy(dp->d_name, bpt, dp->d_namlen);
23455714Skris		(void) closedir(dir);
23555714Skris		dir = NULL;
23655714Skris
237296465Sdelphij		/* Truncate any file name. */
238296465Sdelphij		*bup = '\0';
239296465Sdelphij	}
24055714Skris
241296465Sdelphijnotfound:
242296465Sdelphij	/*
24355714Skris	 * If readdir set errno, use it, not any saved error; otherwise,
244296465Sdelphij	 * didn't find the current directory in its parent directory, set
245296465Sdelphij	 * errno to ENOENT.
246296465Sdelphij	 */
247296465Sdelphij	if (!errno)
248296465Sdelphij		errno = save_errno ? save_errno : ENOENT;
24955714Skris	/* FALLTHROUGH */
250296465Sdelphijerr:
251296465Sdelphij	save_errno = errno;
25255714Skris
253296465Sdelphij	if (ptsize)
254296465Sdelphij		free(pt);
255296465Sdelphij	if (dir)
25655714Skris		(void) closedir(dir);
25755714Skris	free(up);
25855714Skris
25955714Skris	errno = save_errno;
26055714Skris	return (NULL);
261296465Sdelphij}
26255714Skris