getcwd.c revision 23661
11573Srgrimes/*
223661Speter * Copyright (c) 1989, 1991, 1993, 1995
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
523661Speter * This code is derived from software contributed to Berkeley by
623661Speter * Jan-Simon Pendry.
723661Speter *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 3. All advertising materials mentioning features or use of this software
171573Srgrimes *    must display the following acknowledgement:
181573Srgrimes *	This product includes software developed by the University of
191573Srgrimes *	California, Berkeley and its contributors.
201573Srgrimes * 4. Neither the name of the University nor the names of its contributors
211573Srgrimes *    may be used to endorse or promote products derived from this software
221573Srgrimes *    without specific prior written permission.
231573Srgrimes *
241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341573Srgrimes * SUCH DAMAGE.
351573Srgrimes */
361573Srgrimes
371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
3823661Speterstatic char sccsid[] = "@(#)getcwd.c	8.5 (Berkeley) 2/7/95";
391573Srgrimes#endif /* LIBC_SCCS and not lint */
401573Srgrimes
411573Srgrimes#include <sys/param.h>
421573Srgrimes#include <sys/stat.h>
431573Srgrimes
4423661Speter#include <dirent.h>
451573Srgrimes#include <errno.h>
4623661Speter#include <fcntl.h>
471573Srgrimes#include <stdio.h>
481573Srgrimes#include <stdlib.h>
491573Srgrimes#include <string.h>
501573Srgrimes#include <unistd.h>
511573Srgrimes
5223661Speterstatic char *getcwd_physical __P((char *, size_t));
5323661Speter
541573Srgrimes#define	ISDOT(dp) \
551573Srgrimes	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
5617141Sjkh	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
571573Srgrimes
581573Srgrimeschar *
591573Srgrimesgetcwd(pt, size)
601573Srgrimes	char *pt;
611573Srgrimes	size_t size;
621573Srgrimes{
6323661Speter	char *pwd;
6423661Speter	size_t pwdlen;
6523661Speter	dev_t dev;
6623661Speter	ino_t ino;
6723661Speter	struct stat s;
6823661Speter
6923661Speter	/* Check $PWD -- if it's right, it's fast. */
7023661Speter	if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) {
7123661Speter		dev = s.st_dev;
7223661Speter		ino = s.st_ino;
7323661Speter		if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) {
7423661Speter			pwdlen = strlen(pwd);
7523661Speter			if (size != 0) {
7623661Speter				if (pwdlen + 1 > size) {
7723661Speter					errno = ERANGE;
7823661Speter					return (NULL);
7923661Speter				}
8023661Speter			} else if ((pt = malloc(pwdlen + 1)) == NULL)
8123661Speter				return (NULL);
8223661Speter			memmove(pt, pwd, pwdlen);
8323661Speter			pt[pwdlen] = '\0';
8423661Speter			return (pt);
8523661Speter		}
8623661Speter	}
8723661Speter
8823661Speter	return (getcwd_physical(pt, size));
8923661Speter}
9023661Speter
9123661Speter/*
9223661Speter * char *realpath(const char *path, char resolved_path[MAXPATHLEN]);
9323661Speter *
9423661Speter * Find the real name of path, by removing all ".", ".." and symlink
9523661Speter * components.  Returns (resolved) on success, or (NULL) on failure,
9623661Speter * in which case the path which caused trouble is left in (resolved).
9723661Speter */
9823661Speterchar *
9923661Speterrealpath(path, resolved)
10023661Speter	const char *path;
10123661Speter	char *resolved;
10223661Speter{
10323661Speter	struct stat sb;
10423661Speter	int fd, n, rootd, serrno;
10523661Speter	char *p, *q, wbuf[MAXPATHLEN];
10623661Speter
10723661Speter	/* Save the starting point. */
10823661Speter	if ((fd = open(".", O_RDONLY)) < 0) {
10923661Speter		(void)strcpy(resolved, ".");
11023661Speter		return (NULL);
11123661Speter	}
11223661Speter
11323661Speter	/*
11423661Speter	 * Find the dirname and basename from the path to be resolved.
11523661Speter	 * Change directory to the dirname component.
11623661Speter	 * lstat the basename part.
11723661Speter	 *     if it is a symlink, read in the value and loop.
11823661Speter	 *     if it is a directory, then change to that directory.
11923661Speter	 * get the current directory name and append the basename.
12023661Speter	 */
12123661Speter	(void)strncpy(resolved, path, MAXPATHLEN - 1);
12223661Speter	resolved[MAXPATHLEN - 1] = '\0';
12323661Speterloop:
12423661Speter	q = strrchr(resolved, '/');
12523661Speter	if (q != NULL) {
12623661Speter		p = q + 1;
12723661Speter		if (q == resolved)
12823661Speter			q = "/";
12923661Speter		else {
13023661Speter			do {
13123661Speter				--q;
13223661Speter			} while (q > resolved && *q == '/');
13323661Speter			q[1] = '\0';
13423661Speter			q = resolved;
13523661Speter		}
13623661Speter		if (chdir(q) < 0)
13723661Speter			goto err1;
13823661Speter	} else
13923661Speter		p = resolved;
14023661Speter
14123661Speter	/* Deal with the last component. */
14223661Speter	if (*p != '\0' && lstat(p, &sb) == 0) {
14323661Speter		if (S_ISLNK(sb.st_mode)) {
14423661Speter			n = readlink(p, resolved, MAXPATHLEN);
14523661Speter			if (n < 0)
14623661Speter				goto err1;
14723661Speter			resolved[n] = '\0';
14823661Speter			goto loop;
14923661Speter		}
15023661Speter		if (S_ISDIR(sb.st_mode)) {
15123661Speter			if (chdir(p) < 0)
15223661Speter				goto err1;
15323661Speter			p = "";
15423661Speter		}
15523661Speter	}
15623661Speter
15723661Speter	/*
15823661Speter	 * Save the last component name and get the full pathname of
15923661Speter	 * the current directory.
16023661Speter	 */
16123661Speter	(void)strcpy(wbuf, p);
16223661Speter
16323661Speter	/*
16423661Speter	 * Call the inernal internal version of getcwd which
16523661Speter	 * does a physical search rather than using the $PWD short-cut
16623661Speter	 */
16723661Speter	if (getcwd_physical(resolved, MAXPATHLEN) == 0)
16823661Speter		goto err1;
16923661Speter
17023661Speter	/*
17123661Speter	 * Join the two strings together, ensuring that the right thing
17223661Speter	 * happens if the last component is empty, or the dirname is root.
17323661Speter	 */
17423661Speter	if (resolved[0] == '/' && resolved[1] == '\0')
17523661Speter		rootd = 1;
17623661Speter	else
17723661Speter		rootd = 0;
17823661Speter
17923661Speter	if (*wbuf) {
18023661Speter		if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
18123661Speter			errno = ENAMETOOLONG;
18223661Speter			goto err1;
18323661Speter		}
18423661Speter		if (rootd == 0)
18523661Speter			(void)strcat(resolved, "/");
18623661Speter		(void)strcat(resolved, wbuf);
18723661Speter	}
18823661Speter
18923661Speter	/* Go back to where we came from. */
19023661Speter	if (fchdir(fd) < 0) {
19123661Speter		serrno = errno;
19223661Speter		goto err2;
19323661Speter	}
19423661Speter
19523661Speter	/* It's okay if the close fails, what's an fd more or less? */
19623661Speter	(void)close(fd);
19723661Speter	return (resolved);
19823661Speter
19923661Spetererr1:	serrno = errno;
20023661Speter	(void)fchdir(fd);
20123661Spetererr2:	(void)close(fd);
20223661Speter	errno = serrno;
20323661Speter	return (NULL);
20423661Speter}
20523661Speter
20623661Speterstatic char *
20723661Spetergetcwd_physical(pt, size)
20823661Speter	char *pt;
20923661Speter	size_t size;
21023661Speter{
2111573Srgrimes	register struct dirent *dp;
2121573Srgrimes	register DIR *dir;
2131573Srgrimes	register dev_t dev;
2141573Srgrimes	register ino_t ino;
2151573Srgrimes	register int first;
2161573Srgrimes	register char *bpt, *bup;
2171573Srgrimes	struct stat s;
2181573Srgrimes	dev_t root_dev;
2191573Srgrimes	ino_t root_ino;
2206227Sdg	size_t ptsize, upsize;
2211573Srgrimes	int save_errno;
2226227Sdg	char *ept, *eup, *up;
2231573Srgrimes
2241573Srgrimes	/*
2251573Srgrimes	 * If no buffer specified by the user, allocate one as necessary.
2261573Srgrimes	 * If a buffer is specified, the size has to be non-zero.  The path
2271573Srgrimes	 * is built from the end of the buffer backwards.
2281573Srgrimes	 */
2291573Srgrimes	if (pt) {
2301573Srgrimes		ptsize = 0;
2311573Srgrimes		if (!size) {
2321573Srgrimes			errno = EINVAL;
2331573Srgrimes			return (NULL);
2341573Srgrimes		}
2355072Sbde		if (size == 1) {
2365072Sbde			errno = ERANGE;
2375072Sbde			return (NULL);
2385072Sbde		}
2391573Srgrimes		ept = pt + size;
2401573Srgrimes	} else {
2411573Srgrimes		if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
2421573Srgrimes			return (NULL);
2431573Srgrimes		ept = pt + ptsize;
2441573Srgrimes	}
2451573Srgrimes	bpt = ept - 1;
2461573Srgrimes	*bpt = '\0';
2471573Srgrimes
2481573Srgrimes	/*
2491573Srgrimes	 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
2501573Srgrimes	 * Should always be enough (it's 340 levels).  If it's not, allocate
25123661Speter	 * as necessary.  Special case the first stat, it's ".", not "..".
2521573Srgrimes	 */
2531573Srgrimes	if ((up = malloc(upsize = 1024 - 4)) == NULL)
2541573Srgrimes		goto err;
2551573Srgrimes	eup = up + MAXPATHLEN;
2561573Srgrimes	bup = up;
2571573Srgrimes	up[0] = '.';
2581573Srgrimes	up[1] = '\0';
2591573Srgrimes
2601573Srgrimes	/* Save root values, so know when to stop. */
2611573Srgrimes	if (stat("/", &s))
2621573Srgrimes		goto err;
2631573Srgrimes	root_dev = s.st_dev;
2641573Srgrimes	root_ino = s.st_ino;
2651573Srgrimes
2661573Srgrimes	errno = 0;			/* XXX readdir has no error return. */
2671573Srgrimes
2681573Srgrimes	for (first = 1;; first = 0) {
2691573Srgrimes		/* Stat the current level. */
2701573Srgrimes		if (lstat(up, &s))
2711573Srgrimes			goto err;
2721573Srgrimes
2731573Srgrimes		/* Save current node values. */
2741573Srgrimes		ino = s.st_ino;
2751573Srgrimes		dev = s.st_dev;
2761573Srgrimes
2771573Srgrimes		/* Check for reaching root. */
2781573Srgrimes		if (root_dev == dev && root_ino == ino) {
2791573Srgrimes			*--bpt = '/';
2801573Srgrimes			/*
2811573Srgrimes			 * It's unclear that it's a requirement to copy the
2821573Srgrimes			 * path to the beginning of the buffer, but it's always
2831573Srgrimes			 * been that way and stuff would probably break.
2841573Srgrimes			 */
2859272Shsu			bcopy(bpt, pt, ept - bpt);
2861573Srgrimes			free(up);
2871573Srgrimes			return (pt);
2881573Srgrimes		}
2891573Srgrimes
2901573Srgrimes		/*
2911573Srgrimes		 * Build pointer to the parent directory, allocating memory
2921573Srgrimes		 * as necessary.  Max length is 3 for "../", the largest
2931573Srgrimes		 * possible component name, plus a trailing NULL.
2941573Srgrimes		 */
2951573Srgrimes		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
2961573Srgrimes			if ((up = realloc(up, upsize *= 2)) == NULL)
2971573Srgrimes				goto err;
2981573Srgrimes			bup = up;
2991573Srgrimes			eup = up + upsize;
3001573Srgrimes		}
3011573Srgrimes		*bup++ = '.';
3021573Srgrimes		*bup++ = '.';
3031573Srgrimes		*bup = '\0';
3041573Srgrimes
3051573Srgrimes		/* Open and stat parent directory. */
3061573Srgrimes		if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
3071573Srgrimes			goto err;
3081573Srgrimes
3091573Srgrimes		/* Add trailing slash for next directory. */
3101573Srgrimes		*bup++ = '/';
31118971Speter		*bup = '\0';
3121573Srgrimes
3131573Srgrimes		/*
3141573Srgrimes		 * If it's a mount point, have to stat each element because
3151573Srgrimes		 * the inode number in the directory is for the entry in the
3161573Srgrimes		 * parent directory, not the inode number of the mounted file.
3171573Srgrimes		 */
3181573Srgrimes		save_errno = 0;
3191573Srgrimes		if (s.st_dev == dev) {
3201573Srgrimes			for (;;) {
3211573Srgrimes				if (!(dp = readdir(dir)))
3221573Srgrimes					goto notfound;
3231573Srgrimes				if (dp->d_fileno == ino)
3241573Srgrimes					break;
3251573Srgrimes			}
3261573Srgrimes		} else
3271573Srgrimes			for (;;) {
3281573Srgrimes				if (!(dp = readdir(dir)))
3291573Srgrimes					goto notfound;
3301573Srgrimes				if (ISDOT(dp))
3311573Srgrimes					continue;
3321573Srgrimes				bcopy(dp->d_name, bup, dp->d_namlen + 1);
3331573Srgrimes
3341573Srgrimes				/* Save the first error for later. */
3351573Srgrimes				if (lstat(up, &s)) {
3361573Srgrimes					if (!save_errno)
3371573Srgrimes						save_errno = errno;
3381573Srgrimes					errno = 0;
3391573Srgrimes					continue;
3401573Srgrimes				}
3411573Srgrimes				if (s.st_dev == dev && s.st_ino == ino)
3421573Srgrimes					break;
3431573Srgrimes			}
3441573Srgrimes
3451573Srgrimes		/*
3461573Srgrimes		 * Check for length of the current name, preceding slash,
3471573Srgrimes		 * leading slash.
3481573Srgrimes		 */
3495072Sbde		if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
3501573Srgrimes			size_t len, off;
3511573Srgrimes
3521573Srgrimes			if (!ptsize) {
3531573Srgrimes				errno = ERANGE;
3541573Srgrimes				goto err;
3551573Srgrimes			}
3561573Srgrimes			off = bpt - pt;
3571573Srgrimes			len = ept - bpt;
3581573Srgrimes			if ((pt = realloc(pt, ptsize *= 2)) == NULL)
3591573Srgrimes				goto err;
3601573Srgrimes			bpt = pt + off;
3611573Srgrimes			ept = pt + ptsize;
3629272Shsu			bcopy(bpt, ept - len, len);
3631573Srgrimes			bpt = ept - len;
3641573Srgrimes		}
3651573Srgrimes		if (!first)
3661573Srgrimes			*--bpt = '/';
3671573Srgrimes		bpt -= dp->d_namlen;
3681573Srgrimes		bcopy(dp->d_name, bpt, dp->d_namlen);
3691573Srgrimes		(void)closedir(dir);
3701573Srgrimes
3711573Srgrimes		/* Truncate any file name. */
3721573Srgrimes		*bup = '\0';
3731573Srgrimes	}
3741573Srgrimes
3751573Srgrimesnotfound:
3761573Srgrimes	/*
3771573Srgrimes	 * If readdir set errno, use it, not any saved error; otherwise,
3781573Srgrimes	 * didn't find the current directory in its parent directory, set
3791573Srgrimes	 * errno to ENOENT.
3801573Srgrimes	 */
3811573Srgrimes	if (!errno)
3821573Srgrimes		errno = save_errno ? save_errno : ENOENT;
3831573Srgrimes	/* FALLTHROUGH */
3841573Srgrimeserr:
3851573Srgrimes	if (ptsize)
3861573Srgrimes		free(pt);
3871573Srgrimes	free(up);
3881573Srgrimes	return (NULL);
3891573Srgrimes}
390