getcwd.c revision 32530
11573Srgrimes/*
223661Speter * Copyright (c) 1989, 1991, 1993, 1995
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * Redistribution and use in source and binary forms, with or without
61573Srgrimes * modification, are permitted provided that the following conditions
71573Srgrimes * are met:
81573Srgrimes * 1. Redistributions of source code must retain the above copyright
91573Srgrimes *    notice, this list of conditions and the following disclaimer.
101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111573Srgrimes *    notice, this list of conditions and the following disclaimer in the
121573Srgrimes *    documentation and/or other materials provided with the distribution.
131573Srgrimes * 3. All advertising materials mentioning features or use of this software
141573Srgrimes *    must display the following acknowledgement:
151573Srgrimes *	This product includes software developed by the University of
161573Srgrimes *	California, Berkeley and its contributors.
171573Srgrimes * 4. Neither the name of the University nor the names of its contributors
181573Srgrimes *    may be used to endorse or promote products derived from this software
191573Srgrimes *    without specific prior written permission.
201573Srgrimes *
211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311573Srgrimes * SUCH DAMAGE.
321573Srgrimes */
331573Srgrimes
341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
3523661Speterstatic char sccsid[] = "@(#)getcwd.c	8.5 (Berkeley) 2/7/95";
361573Srgrimes#endif /* LIBC_SCCS and not lint */
371573Srgrimes
381573Srgrimes#include <sys/param.h>
391573Srgrimes#include <sys/stat.h>
401573Srgrimes
4123661Speter#include <dirent.h>
421573Srgrimes#include <errno.h>
4323661Speter#include <fcntl.h>
441573Srgrimes#include <stdio.h>
451573Srgrimes#include <stdlib.h>
461573Srgrimes#include <string.h>
471573Srgrimes#include <unistd.h>
4829487Speter#include <signal.h>
491573Srgrimes
501573Srgrimes#define	ISDOT(dp) \
511573Srgrimes	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
5217141Sjkh	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
531573Srgrimes
5429487Speterstatic int have__getcwd = 1;	/* 0 = no, 1 = perhaps, 2 = yes */
5529487Speter
561573Srgrimeschar *
571573Srgrimesgetcwd(pt, size)
581573Srgrimes	char *pt;
591573Srgrimes	size_t size;
601573Srgrimes{
611573Srgrimes	register struct dirent *dp;
6228235Sdg	register DIR *dir = NULL;
631573Srgrimes	register dev_t dev;
641573Srgrimes	register ino_t ino;
651573Srgrimes	register int first;
661573Srgrimes	register char *bpt, *bup;
671573Srgrimes	struct stat s;
681573Srgrimes	dev_t root_dev;
691573Srgrimes	ino_t root_ino;
706227Sdg	size_t ptsize, upsize;
711573Srgrimes	int save_errno;
7229462Sphk	char *ept, *eup, *up, c;
731573Srgrimes
741573Srgrimes	/*
751573Srgrimes	 * If no buffer specified by the user, allocate one as necessary.
761573Srgrimes	 * If a buffer is specified, the size has to be non-zero.  The path
771573Srgrimes	 * is built from the end of the buffer backwards.
781573Srgrimes	 */
791573Srgrimes	if (pt) {
801573Srgrimes		ptsize = 0;
811573Srgrimes		if (!size) {
821573Srgrimes			errno = EINVAL;
831573Srgrimes			return (NULL);
841573Srgrimes		}
855072Sbde		if (size == 1) {
865072Sbde			errno = ERANGE;
875072Sbde			return (NULL);
885072Sbde		}
891573Srgrimes		ept = pt + size;
901573Srgrimes	} else {
911573Srgrimes		if ((pt = malloc(ptsize = 1024 - 4)) == NULL)
921573Srgrimes			return (NULL);
931573Srgrimes		ept = pt + ptsize;
941573Srgrimes	}
9529487Speter	if (have__getcwd) {
9629487Speter		struct sigaction sa, osa;
9729487Speter		int sigsys_installed = 0;
9829487Speter		int ret;
9929487Speter
10029487Speter		if (have__getcwd == 1) {	/* unsure? */
10129487Speter			bzero(&sa, sizeof(sa));
10229487Speter			sa.sa_handler = SIG_IGN;
10329487Speter			if (sigaction(SIGSYS, &sa, &osa) >= 0)
10429487Speter				sigsys_installed = 1;
10529487Speter		}
10629487Speter		ret = __getcwd(pt, ept - pt);
10729487Speter		if (sigsys_installed == 1) {
10829487Speter			int oerrno = errno;
10929487Speter			sigaction(SIGSYS, &osa, NULL);
11029487Speter			errno = oerrno;
11129487Speter		}
11229487Speter		/* XXX a bogus syscall seems to return EINVAL(!) */
11329487Speter		if (ret < 0 && (errno == ENOSYS || errno == EINVAL))
11429487Speter			have__getcwd = 0;
11529487Speter		else if (have__getcwd == 1)
11629487Speter			have__getcwd = 2;	/* yep, remember we have it */
11729487Speter		if (ret == 0) {
11829487Speter			if (*pt != '/') {
11929487Speter				bpt = pt;
12029487Speter				ept = pt + strlen(pt) - 1;
12129487Speter				while (bpt < ept) {
12229487Speter					c = *bpt;
12329487Speter					*bpt++ = *ept;
12429487Speter					*ept-- = c;
12529487Speter				}
12629476Sphk			}
12729487Speter			return (pt);
12829392Sphk		}
12929392Sphk	}
1301573Srgrimes	bpt = ept - 1;
1311573Srgrimes	*bpt = '\0';
1321573Srgrimes
1331573Srgrimes	/*
1341573Srgrimes	 * Allocate bytes (1024 - malloc space) for the string of "../"'s.
1351573Srgrimes	 * Should always be enough (it's 340 levels).  If it's not, allocate
13623661Speter	 * as necessary.  Special case the first stat, it's ".", not "..".
1371573Srgrimes	 */
1381573Srgrimes	if ((up = malloc(upsize = 1024 - 4)) == NULL)
1391573Srgrimes		goto err;
1401573Srgrimes	eup = up + MAXPATHLEN;
1411573Srgrimes	bup = up;
1421573Srgrimes	up[0] = '.';
1431573Srgrimes	up[1] = '\0';
1441573Srgrimes
1451573Srgrimes	/* Save root values, so know when to stop. */
1461573Srgrimes	if (stat("/", &s))
1471573Srgrimes		goto err;
1481573Srgrimes	root_dev = s.st_dev;
1491573Srgrimes	root_ino = s.st_ino;
1501573Srgrimes
1511573Srgrimes	errno = 0;			/* XXX readdir has no error return. */
1521573Srgrimes
1531573Srgrimes	for (first = 1;; first = 0) {
1541573Srgrimes		/* Stat the current level. */
1551573Srgrimes		if (lstat(up, &s))
1561573Srgrimes			goto err;
1571573Srgrimes
1581573Srgrimes		/* Save current node values. */
1591573Srgrimes		ino = s.st_ino;
1601573Srgrimes		dev = s.st_dev;
1611573Srgrimes
1621573Srgrimes		/* Check for reaching root. */
1631573Srgrimes		if (root_dev == dev && root_ino == ino) {
1641573Srgrimes			*--bpt = '/';
1651573Srgrimes			/*
1661573Srgrimes			 * It's unclear that it's a requirement to copy the
1671573Srgrimes			 * path to the beginning of the buffer, but it's always
1681573Srgrimes			 * been that way and stuff would probably break.
1691573Srgrimes			 */
1709272Shsu			bcopy(bpt, pt, ept - bpt);
1711573Srgrimes			free(up);
1721573Srgrimes			return (pt);
1731573Srgrimes		}
1741573Srgrimes
1751573Srgrimes		/*
1761573Srgrimes		 * Build pointer to the parent directory, allocating memory
1771573Srgrimes		 * as necessary.  Max length is 3 for "../", the largest
1781573Srgrimes		 * possible component name, plus a trailing NULL.
1791573Srgrimes		 */
1801573Srgrimes		if (bup + 3  + MAXNAMLEN + 1 >= eup) {
1811573Srgrimes			if ((up = realloc(up, upsize *= 2)) == NULL)
1821573Srgrimes				goto err;
1831573Srgrimes			bup = up;
1841573Srgrimes			eup = up + upsize;
1851573Srgrimes		}
1861573Srgrimes		*bup++ = '.';
1871573Srgrimes		*bup++ = '.';
1881573Srgrimes		*bup = '\0';
1891573Srgrimes
1901573Srgrimes		/* Open and stat parent directory. */
1911573Srgrimes		if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
1921573Srgrimes			goto err;
1931573Srgrimes
1941573Srgrimes		/* Add trailing slash for next directory. */
1951573Srgrimes		*bup++ = '/';
19618971Speter		*bup = '\0';
1971573Srgrimes
1981573Srgrimes		/*
1991573Srgrimes		 * If it's a mount point, have to stat each element because
2001573Srgrimes		 * the inode number in the directory is for the entry in the
2011573Srgrimes		 * parent directory, not the inode number of the mounted file.
2021573Srgrimes		 */
2031573Srgrimes		save_errno = 0;
2041573Srgrimes		if (s.st_dev == dev) {
2051573Srgrimes			for (;;) {
2061573Srgrimes				if (!(dp = readdir(dir)))
2071573Srgrimes					goto notfound;
2081573Srgrimes				if (dp->d_fileno == ino)
2091573Srgrimes					break;
2101573Srgrimes			}
2111573Srgrimes		} else
2121573Srgrimes			for (;;) {
2131573Srgrimes				if (!(dp = readdir(dir)))
2141573Srgrimes					goto notfound;
2151573Srgrimes				if (ISDOT(dp))
2161573Srgrimes					continue;
2171573Srgrimes				bcopy(dp->d_name, bup, dp->d_namlen + 1);
2181573Srgrimes
2191573Srgrimes				/* Save the first error for later. */
2201573Srgrimes				if (lstat(up, &s)) {
2211573Srgrimes					if (!save_errno)
2221573Srgrimes						save_errno = errno;
2231573Srgrimes					errno = 0;
2241573Srgrimes					continue;
2251573Srgrimes				}
2261573Srgrimes				if (s.st_dev == dev && s.st_ino == ino)
2271573Srgrimes					break;
2281573Srgrimes			}
2291573Srgrimes
2301573Srgrimes		/*
2311573Srgrimes		 * Check for length of the current name, preceding slash,
2321573Srgrimes		 * leading slash.
2331573Srgrimes		 */
2345072Sbde		if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
2351573Srgrimes			size_t len, off;
2361573Srgrimes
2371573Srgrimes			if (!ptsize) {
2381573Srgrimes				errno = ERANGE;
2391573Srgrimes				goto err;
2401573Srgrimes			}
2411573Srgrimes			off = bpt - pt;
2421573Srgrimes			len = ept - bpt;
2431573Srgrimes			if ((pt = realloc(pt, ptsize *= 2)) == NULL)
2441573Srgrimes				goto err;
2451573Srgrimes			bpt = pt + off;
2461573Srgrimes			ept = pt + ptsize;
2479272Shsu			bcopy(bpt, ept - len, len);
2481573Srgrimes			bpt = ept - len;
2491573Srgrimes		}
2501573Srgrimes		if (!first)
2511573Srgrimes			*--bpt = '/';
2521573Srgrimes		bpt -= dp->d_namlen;
2531573Srgrimes		bcopy(dp->d_name, bpt, dp->d_namlen);
25428235Sdg		(void) closedir(dir);
25528235Sdg		dir = NULL;
2561573Srgrimes
2571573Srgrimes		/* Truncate any file name. */
2581573Srgrimes		*bup = '\0';
2591573Srgrimes	}
2601573Srgrimes
2611573Srgrimesnotfound:
2621573Srgrimes	/*
2631573Srgrimes	 * If readdir set errno, use it, not any saved error; otherwise,
2641573Srgrimes	 * didn't find the current directory in its parent directory, set
2651573Srgrimes	 * errno to ENOENT.
2661573Srgrimes	 */
2671573Srgrimes	if (!errno)
2681573Srgrimes		errno = save_errno ? save_errno : ENOENT;
2691573Srgrimes	/* FALLTHROUGH */
2701573Srgrimeserr:
27132530Smckay	save_errno = errno;
27232530Smckay
2731573Srgrimes	if (ptsize)
2741573Srgrimes		free(pt);
27528235Sdg	if (dir)
27628235Sdg		(void) closedir(dir);
2771573Srgrimes	free(up);
27832530Smckay
27932530Smckay	errno = save_errno;
2801573Srgrimes	return (NULL);
2811573Srgrimes}
282