getcwd.c revision 235647
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 * 4. Neither the name of the University nor the names of its contributors
141573Srgrimes *    may be used to endorse or promote products derived from this software
151573Srgrimes *    without specific prior written permission.
161573Srgrimes *
171573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271573Srgrimes * SUCH DAMAGE.
281573Srgrimes */
291573Srgrimes
301573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
3123661Speterstatic char sccsid[] = "@(#)getcwd.c	8.5 (Berkeley) 2/7/95";
321573Srgrimes#endif /* LIBC_SCCS and not lint */
3390041Sobrien#include <sys/cdefs.h>
3490041Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/getcwd.c 235647 2012-05-19 12:44:27Z gleb $");
351573Srgrimes
3671579Sdeischen#include "namespace.h"
371573Srgrimes#include <sys/param.h>
381573Srgrimes#include <sys/stat.h>
391573Srgrimes
4023661Speter#include <dirent.h>
411573Srgrimes#include <errno.h>
4223661Speter#include <fcntl.h>
431573Srgrimes#include <stdio.h>
441573Srgrimes#include <stdlib.h>
451573Srgrimes#include <string.h>
461573Srgrimes#include <unistd.h>
4771579Sdeischen#include "un-namespace.h"
481573Srgrimes
49235647Sgleb#include "gen-private.h"
50235647Sgleb
511573Srgrimes#define	ISDOT(dp) \
521573Srgrimes	(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
5317141Sjkh	    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
541573Srgrimes
55109039Stjrextern int __getcwd(char *, size_t);
56109039Stjr
571573Srgrimeschar *
581573Srgrimesgetcwd(pt, size)
591573Srgrimes	char *pt;
601573Srgrimes	size_t size;
611573Srgrimes{
6290041Sobrien	struct dirent *dp;
6390041Sobrien	DIR *dir = NULL;
6490041Sobrien	dev_t dev;
6590041Sobrien	ino_t ino;
6690041Sobrien	int first;
67198053Sjilles	char *bpt;
681573Srgrimes	struct stat s;
691573Srgrimes	dev_t root_dev;
701573Srgrimes	ino_t root_ino;
71198053Sjilles	size_t ptsize;
721573Srgrimes	int save_errno;
73198053Sjilles	char *ept, c;
74198053Sjilles	int fd;
751573Srgrimes
761573Srgrimes	/*
771573Srgrimes	 * If no buffer specified by the user, allocate one as necessary.
781573Srgrimes	 * If a buffer is specified, the size has to be non-zero.  The path
791573Srgrimes	 * is built from the end of the buffer backwards.
801573Srgrimes	 */
811573Srgrimes	if (pt) {
821573Srgrimes		ptsize = 0;
831573Srgrimes		if (!size) {
841573Srgrimes			errno = EINVAL;
851573Srgrimes			return (NULL);
861573Srgrimes		}
875072Sbde		if (size == 1) {
885072Sbde			errno = ERANGE;
895072Sbde			return (NULL);
905072Sbde		}
911573Srgrimes		ept = pt + size;
921573Srgrimes	} else {
93150172Sache		if ((pt = malloc(ptsize = PATH_MAX)) == NULL)
941573Srgrimes			return (NULL);
951573Srgrimes		ept = pt + ptsize;
961573Srgrimes	}
9765468Speter	if (__getcwd(pt, ept - pt) == 0) {
9865468Speter		if (*pt != '/') {
9965468Speter			bpt = pt;
10065468Speter			ept = pt + strlen(pt) - 1;
10165468Speter			while (bpt < ept) {
10265468Speter				c = *bpt;
10365468Speter				*bpt++ = *ept;
10465468Speter				*ept-- = c;
10529476Sphk			}
10629392Sphk		}
10765468Speter		return (pt);
10829392Sphk	}
1091573Srgrimes	bpt = ept - 1;
1101573Srgrimes	*bpt = '\0';
1111573Srgrimes
1121573Srgrimes	/* Save root values, so know when to stop. */
1131573Srgrimes	if (stat("/", &s))
1141573Srgrimes		goto err;
1151573Srgrimes	root_dev = s.st_dev;
1161573Srgrimes	root_ino = s.st_ino;
1171573Srgrimes
1181573Srgrimes	errno = 0;			/* XXX readdir has no error return. */
1191573Srgrimes
1201573Srgrimes	for (first = 1;; first = 0) {
1211573Srgrimes		/* Stat the current level. */
122235647Sgleb		if (dir != NULL ? _fstat(_dirfd(dir), &s) : lstat(".", &s))
1231573Srgrimes			goto err;
1241573Srgrimes
1251573Srgrimes		/* Save current node values. */
1261573Srgrimes		ino = s.st_ino;
1271573Srgrimes		dev = s.st_dev;
1281573Srgrimes
1291573Srgrimes		/* Check for reaching root. */
1301573Srgrimes		if (root_dev == dev && root_ino == ino) {
1311573Srgrimes			*--bpt = '/';
1321573Srgrimes			/*
1331573Srgrimes			 * It's unclear that it's a requirement to copy the
1341573Srgrimes			 * path to the beginning of the buffer, but it's always
1351573Srgrimes			 * been that way and stuff would probably break.
1361573Srgrimes			 */
1379272Shsu			bcopy(bpt, pt, ept - bpt);
138198053Sjilles			if (dir)
139198053Sjilles				(void) closedir(dir);
1401573Srgrimes			return (pt);
1411573Srgrimes		}
1421573Srgrimes
1431573Srgrimes		/* Open and stat parent directory. */
144235647Sgleb		fd = _openat(dir != NULL ? _dirfd(dir) : AT_FDCWD,
145198053Sjilles				"..", O_RDONLY);
146198053Sjilles		if (fd == -1)
1471573Srgrimes			goto err;
148198053Sjilles		if (dir)
149198053Sjilles			(void) closedir(dir);
150235647Sgleb		if (!(dir = fdopendir(fd)) || _fstat(_dirfd(dir), &s)) {
151198053Sjilles			_close(fd);
152198053Sjilles			goto err;
153198053Sjilles		}
1541573Srgrimes
1551573Srgrimes		/*
1561573Srgrimes		 * If it's a mount point, have to stat each element because
1571573Srgrimes		 * the inode number in the directory is for the entry in the
1581573Srgrimes		 * parent directory, not the inode number of the mounted file.
1591573Srgrimes		 */
1601573Srgrimes		save_errno = 0;
1611573Srgrimes		if (s.st_dev == dev) {
1621573Srgrimes			for (;;) {
1631573Srgrimes				if (!(dp = readdir(dir)))
1641573Srgrimes					goto notfound;
1651573Srgrimes				if (dp->d_fileno == ino)
1661573Srgrimes					break;
1671573Srgrimes			}
1681573Srgrimes		} else
1691573Srgrimes			for (;;) {
1701573Srgrimes				if (!(dp = readdir(dir)))
1711573Srgrimes					goto notfound;
1721573Srgrimes				if (ISDOT(dp))
1731573Srgrimes					continue;
1741573Srgrimes
1751573Srgrimes				/* Save the first error for later. */
176235647Sgleb				if (fstatat(_dirfd(dir), dp->d_name, &s,
177198053Sjilles				    AT_SYMLINK_NOFOLLOW)) {
1781573Srgrimes					if (!save_errno)
1791573Srgrimes						save_errno = errno;
1801573Srgrimes					errno = 0;
1811573Srgrimes					continue;
1821573Srgrimes				}
1831573Srgrimes				if (s.st_dev == dev && s.st_ino == ino)
1841573Srgrimes					break;
1851573Srgrimes			}
1861573Srgrimes
1871573Srgrimes		/*
1881573Srgrimes		 * Check for length of the current name, preceding slash,
1891573Srgrimes		 * leading slash.
1901573Srgrimes		 */
191150297Sache		while (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
1921573Srgrimes			size_t len, off;
1931573Srgrimes
1941573Srgrimes			if (!ptsize) {
1951573Srgrimes				errno = ERANGE;
1961573Srgrimes				goto err;
1971573Srgrimes			}
1981573Srgrimes			off = bpt - pt;
1991573Srgrimes			len = ept - bpt;
200109040Stjr			if ((pt = reallocf(pt, ptsize *= 2)) == NULL)
2011573Srgrimes				goto err;
2021573Srgrimes			bpt = pt + off;
2031573Srgrimes			ept = pt + ptsize;
2049272Shsu			bcopy(bpt, ept - len, len);
2051573Srgrimes			bpt = ept - len;
2061573Srgrimes		}
2071573Srgrimes		if (!first)
2081573Srgrimes			*--bpt = '/';
2091573Srgrimes		bpt -= dp->d_namlen;
2101573Srgrimes		bcopy(dp->d_name, bpt, dp->d_namlen);
2111573Srgrimes	}
2121573Srgrimes
2131573Srgrimesnotfound:
2141573Srgrimes	/*
2151573Srgrimes	 * If readdir set errno, use it, not any saved error; otherwise,
2161573Srgrimes	 * didn't find the current directory in its parent directory, set
2171573Srgrimes	 * errno to ENOENT.
2181573Srgrimes	 */
2191573Srgrimes	if (!errno)
2201573Srgrimes		errno = save_errno ? save_errno : ENOENT;
2211573Srgrimes	/* FALLTHROUGH */
2221573Srgrimeserr:
22332530Smckay	save_errno = errno;
22432530Smckay
2251573Srgrimes	if (ptsize)
2261573Srgrimes		free(pt);
22728235Sdg	if (dir)
22828235Sdg		(void) closedir(dir);
22932530Smckay
23032530Smckay	errno = save_errno;
2311573Srgrimes	return (NULL);
2321573Srgrimes}
233